diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 50d4c035d30a1..ad0cd90b665d8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -465,12 +465,48 @@ static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal, cir::FuncOp directFuncOp, - const SmallVectorImpl &cirCallArgs, + const SmallVectorImpl &cirCallArgs, bool isInvoke, const mlir::NamedAttrList &attrs) { CIRGenBuilderTy &builder = cgf.getBuilder(); assert(!cir::MissingFeatures::opCallSurroundingTry()); - assert(!cir::MissingFeatures::invokeOp()); + + if (isInvoke) { + // This call can throw, few options: + // - If this call does not have an associated cir.try, use the + // one provided by InvokeDest, + // - User written try/catch clauses require calls to handle + // exceptions under cir.try. + + // 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.createTryCallOp(callLoc, directFuncOp, cirCallArgs); + + (void)cgf.getInvokeDest(tryOp); + + return callOpWithExceptions; + } assert(builder.getInsertionBlock() && "expected valid basic block"); @@ -628,10 +664,11 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, indirectFuncVal = calleePtr->getResult(0); } + bool isInvoke = isInvokeDest(); mlir::Location callLoc = loc; cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, - cirCallArgs, attrs); + cirCallArgs, isInvoke, attrs); if (callOp) *callOp = theCall; diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 851328a7db680..3550a78cc1816 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -147,8 +147,8 @@ void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) { assert(!cir::MissingFeatures::innermostEHScope()); - EHCleanupScope *scope = new (buffer) - EHCleanupScope(size, branchFixups.size(), innermostNormalCleanup); + EHCleanupScope *scope = new (buffer) EHCleanupScope( + size, branchFixups.size(), innermostNormalCleanup, innermostEHScope); if (isNormalCleanup) innermostNormalCleanup = stable_begin(); @@ -188,10 +188,20 @@ void EHScopeStack::popCleanup() { } } +bool EHScopeStack::requiresLandingPad() const { + for (stable_iterator si = getInnermostEHScope(); si != stable_end();) { + // TODO(cir): Skip lifetime markers. + assert(!cir::MissingFeatures::emitLifetimeMarkers()); + return true; + } + return false; +} + EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) { char *buffer = allocate(EHCatchScope::getSizeForNumHandlers(numHandlers)); - assert(!cir::MissingFeatures::innermostEHScope()); - EHCatchScope *scope = new (buffer) EHCatchScope(numHandlers); + EHCatchScope *scope = + new (buffer) EHCatchScope(numHandlers, innermostEHScope); + innermostEHScope = stable_begin(); return scope; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h index 61a09a59b05c0..4e4e913574991 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h @@ -30,12 +30,16 @@ struct CatchTypeInfo { /// A protected scope for zero-cost EH handling. class EHScope { + EHScopeStack::stable_iterator enclosingEHScope; + class CommonBitFields { friend class EHScope; unsigned kind : 3; }; enum { NumCommonBits = 3 }; + bool isScopeMayThrow; + protected: class CatchBitFields { friend class EHCatchScope; @@ -79,7 +83,10 @@ class EHScope { public: enum Kind { Cleanup, Catch, Terminate, Filter }; - EHScope(Kind kind) { commonBits.kind = kind; } + EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope) + : enclosingEHScope(enclosingEHScope) { + commonBits.kind = kind; + } Kind getKind() const { return static_cast(commonBits.kind); } @@ -87,8 +94,13 @@ class EHScope { // Traditional LLVM codegen also checks for `!block->use_empty()`, but // in CIRGen the block content is not important, just used as a way to // signal `hasEHBranches`. - assert(!cir::MissingFeatures::ehstackBranches()); - return false; + return isScopeMayThrow; + } + + void setMayThrow(bool mayThrow) { isScopeMayThrow = mayThrow; } + + EHScopeStack::stable_iterator getEnclosingEHScope() const { + return enclosingEHScope; } }; @@ -111,6 +123,8 @@ class EHCatchScope : public EHScope { /// The catch handler for this type. mlir::Region *region; + + bool isCatchAll() const { return type.rtti == nullptr; } }; private: @@ -118,12 +132,18 @@ class EHCatchScope : public EHScope { Handler *getHandlers() { return reinterpret_cast(this + 1); } + const Handler *getHandlers() const { + return reinterpret_cast(this + 1); + } + public: static size_t getSizeForNumHandlers(unsigned n) { return sizeof(EHCatchScope) + n * sizeof(Handler); } - EHCatchScope(unsigned numHandlers) : EHScope(Catch) { + EHCatchScope(unsigned numHandlers, + EHScopeStack::stable_iterator enclosingEHScope) + : EHScope(Catch, enclosingEHScope) { catchBits.numHandlers = numHandlers; assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?"); } @@ -136,6 +156,11 @@ class EHCatchScope : public EHScope { getHandlers()[i].region = region; } + 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 @@ -144,6 +169,10 @@ class EHCatchScope : public EHScope { // 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; } @@ -176,9 +205,10 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope } EHCleanupScope(unsigned cleanupSize, unsigned fixupDepth, - EHScopeStack::stable_iterator enclosingNormal) - : EHScope(EHScope::Cleanup), enclosingNormal(enclosingNormal), - fixupDepth(fixupDepth) { + EHScopeStack::stable_iterator enclosingNormal, + EHScopeStack::stable_iterator enclosingEH) + : EHScope(EHScope::Cleanup, enclosingEH), + enclosingNormal(enclosingNormal), fixupDepth(fixupDepth) { // TODO(cir): When exception handling is upstreamed, isNormalCleanup and // isEHCleanup will be arguments to the constructor. cleanupBits.isNormalCleanup = true; @@ -235,13 +265,45 @@ class EHScopeStack::iterator { EHScope *get() const { return reinterpret_cast(ptr); } + EHScope *operator->() const { return get(); } EHScope &operator*() const { return *get(); } + + iterator &operator++() { + size_t size; + switch (get()->getKind()) { + case EHScope::Catch: + size = EHCatchScope::getSizeForNumHandlers( + static_cast(get())->getNumHandlers()); + break; + + case EHScope::Filter: + llvm_unreachable("EHScopeStack::iterator Filter"); + break; + + case EHScope::Cleanup: + llvm_unreachable("EHScopeStack::iterator Cleanup"); + break; + + case EHScope::Terminate: + llvm_unreachable("EHScopeStack::iterator Terminate"); + break; + } + ptr += llvm::alignTo(size, ScopeStackAlignment); + return *this; + } + + bool operator==(iterator other) const { return ptr == other.ptr; } + bool operator!=(iterator other) const { return ptr != other.ptr; } }; inline EHScopeStack::iterator EHScopeStack::begin() const { return iterator(startOfData); } +inline EHScopeStack::iterator EHScopeStack::end() const { + return iterator(endOfBuffer); +} + inline EHScopeStack::iterator EHScopeStack::find(stable_iterator savePoint) const { assert(savePoint.isValid() && "finding invalid savepoint"); @@ -254,7 +316,7 @@ inline void EHScopeStack::popCatch() { assert(!empty() && "popping exception stack when not empty"); EHCatchScope &scope = llvm::cast(*begin()); - assert(!cir::MissingFeatures::innermostEHScope()); + innermostEHScope = scope.getEnclosingEHScope(); deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers())); } diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 67f46ffde8fda..700e5e0c67c45 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -14,6 +14,7 @@ #include "CIRGenFunction.h" #include "clang/AST/StmtVisitor.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace clang::CIRGen; @@ -354,6 +355,33 @@ void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp, } } +/// 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: WASM personality"); + return; + } + + if (EHPersonality::get(cgf).usesFuncletPads()) { + cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads"); + return; + } + + assert(catchScope.mayThrow() && + "Expected catchScope that may throw exception"); + + // If there's only a single catch-all, getEHDispatchBlock returned + // that catch-all as the dispatch block. + if (catchScope.getNumHandlers() == 1 && + catchScope.getHandler(0).isCatchAll()) { + return; + } + + cgf.cgm.errorNYI("emitCatchDispatchBlock: non-catch all handler"); +} + void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) { unsigned numHandlers = s.getNumHandlers(); EHCatchScope &catchScope = cast(*ehStack.begin()); @@ -382,5 +410,279 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) { return; } - cgm.errorNYI("exitCXXTryStmt: Required catch"); + // 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 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(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 (unsigned i = numHandlers; i != 0; --i) { + hasCatchAll |= handlers[i - 1].isCatchAll(); + mlir::Region *catchRegion = handlers[i - 1].region; + + mlir::OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointToStart(&catchRegion->front()); + + const CXXCatchStmt *catchStmt = s.getHandler(i - 1); + + // Enter a cleanup scope, including the catch variable and the + // end-catch. + RunCleanupsScope catchScope(*this); + + // Initialize the catch variable and set up the cleanups. + // TODO: emitBeginCatch + + // Emit the PGO counter increment. + assert(!cir::MissingFeatures::incrementProfileCounter()); + + // Perform the body of the catch. + mlir::LogicalResult emitResult = + emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true); + assert(emitResult.succeeded() && "failed to emit catch handler block"); + + // TODO(cir): This yeild should replaced by CatchParamOp once it upstreamed + cir::YieldOp::create(builder, tryOp->getLoc()); + + // [except.handle]p11: + // The currently handled exception is rethrown if control + // 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; + } + + // 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"); + } + + assert(!cir::MissingFeatures::incrementProfileCounter()); +} + +mlir::Operation *CIRGenFunction::emitLandingPad(cir::TryOp tryOp) { + assert(ehStack.requiresLandingPad()); + 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("emitLandingPad: 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::SmallVector 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; + } + + EHCatchScope &catchScope = cast(*i); + for (unsigned handlerIdx = 0, he = catchScope.getNumHandlers(); + handlerIdx != he; ++handlerIdx) { + EHCatchScope::Handler handler = catchScope.getHandler(handlerIdx); + 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; + goto done; + } + + cgm.errorNYI("emitLandingPad: non catch-all"); + return {}; + } + + goto done; + } + + done: + if (hasCatchAll) { + handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext())); + } else { + cgm.errorNYI("emitLandingPad: non catch-all"); + return {}; + } + + // 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, + // getEHDispatchBlock is used to populate blocks for later filing during + // cleanup handling. + (void)getEHDispatchBlock(ehStack.getInnermostEHScope(), tryOp); + + return 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, we keep this around since other parts of CIRGen use +// getCachedEHDispatchBlock to infer state. +mlir::Block * +CIRGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator scope, + cir::TryOp tryOp) { + if (EHPersonality::get(*this).usesFuncletPads()) { + cgm.errorNYI("getEHDispatchBlock: usesFuncletPads"); + 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 different 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: { + // LLVM does some optimization with branches here, CIR just keep track of + // the corresponding calls. + EHCatchScope &catchScope = cast(ehScope); + if (catchScope.getNumHandlers() == 1 && + catchScope.getHandler(0).isCatchAll()) { + mayThrow = true; + break; + } + cgm.errorNYI("getEHDispatchBlock: mayThrow non-catch all"); + return {}; + } + 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 {}; +} + +bool CIRGenFunction::isInvokeDest() { + if (!ehStack.requiresLandingPad()) + return false; + + // If exceptions are disabled/ignored and SEH is not in use, then there is no + // invoke destination. SEH "works" even if exceptions are off. In practice, + // this means that C++ destructors and other EH cleanups don't run, which is + // consistent with MSVC's behavior, except in the presence of -EHa + const LangOptions &lo = cgm.getLangOpts(); + if (!lo.Exceptions || lo.IgnoreExceptions) { + cgm.errorNYI("isInvokeDest: no exceptions or ignore exception"); + return false; + } + + // CUDA device code doesn't have exceptions. + if (lo.CUDA && lo.CUDAIsDevice) + return false; + + return true; +} + +mlir::Operation *CIRGenFunction::getInvokeDestImpl(cir::TryOp tryOp) { + assert(ehStack.requiresLandingPad()); + assert(!ehStack.empty()); + + // TODO(cir): add personality function + + // CIR does not cache landing pads. + const EHPersonality &personality = EHPersonality::get(*this); + + mlir::Operation *lp = nullptr; + if (personality.usesFuncletPads()) { + cgm.errorNYI("getInvokeDestImpl: usesFuncletPads"); + } else { + lp = emitLandingPad(tryOp); + } + + return lp; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 58feb36f78f23..26e2c59707eae 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -400,6 +400,16 @@ void CIRGenFunction::LexicalScope::emitImplicitReturn() { (void)emitReturn(localScope->endLoc); } +cir::TryOp CIRGenFunction::LexicalScope::getClosestTryParent() { + LexicalScope *scope = this; + while (scope) { + if (scope->isTry()) + return scope->getTry(); + scope = scope->parentScope; + } + return nullptr; +} + void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, cir::FuncOp fn, cir::FuncType funcType, FunctionArgList args, SourceLocation loc, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index c3fcd1a69a88e..d9b3c217e6d57 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -916,10 +916,23 @@ class CIRGenFunction : public CIRGenTypeCache { return false; } + mlir::Block *getEHDispatchBlock(EHScopeStack::stable_iterator scope, + cir::TryOp tryOp); + /// The cleanup depth enclosing all the cleanups associated with the /// parameters. EHScopeStack::stable_iterator prologueCleanupDepth; + mlir::Operation *getInvokeDestImpl(cir::TryOp tryOp); + mlir::Operation *getInvokeDest(cir::TryOp tryOp) { + if (!ehStack.requiresLandingPad()) + return nullptr; + // Return the respective cir.try, this can be used to compute + // any other relevant information. + return getInvokeDestImpl(tryOp); + } + bool isInvokeDest(); + /// Takes the old cleanup stack size and emits the cleanup blocks /// that have been added. void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth); @@ -1063,7 +1076,7 @@ class CIRGenFunction : public CIRGenTypeCache { bool isSwitch() { return scopeKind == Kind::Switch; } bool isTernary() { return scopeKind == Kind::Ternary; } bool isTry() { return scopeKind == Kind::Try; } - + cir::TryOp getClosestTryParent(); void setAsGlobalInit() { scopeKind = Kind::GlobalInit; } void setAsSwitch() { scopeKind = Kind::Switch; } void setAsTernary() { scopeKind = Kind::Ternary; } @@ -1562,6 +1575,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitLambdaDelegatingInvokeBody(const CXXMethodDecl *md); void emitLambdaStaticInvokeBody(const CXXMethodDecl *md); + mlir::Operation *emitLandingPad(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 4198c23c9cbed..699ef0b799c37 100644 --- a/clang/lib/CIR/CodeGen/EHScopeStack.h +++ b/clang/lib/CIR/CodeGen/EHScopeStack.h @@ -155,6 +155,9 @@ class EHScopeStack { /// The innermost normal cleanup on the stack. stable_iterator innermostNormalCleanup = stable_end(); + /// The innermost EH scope on the stack. + stable_iterator innermostEHScope = stable_end(); + /// The CGF this Stack belong to CIRGenFunction *cgf = nullptr; @@ -214,6 +217,8 @@ class EHScopeStack { /// Determines whether the exception-scopes stack is empty. bool empty() const { return startOfData == endOfBuffer; } + bool requiresLandingPad() const; + /// Determines whether there are any normal cleanups on the stack. bool hasNormalCleanups() const { return innermostNormalCleanup != stable_end(); @@ -226,6 +231,8 @@ class EHScopeStack { } stable_iterator getInnermostActiveNormalCleanup() const; + stable_iterator getInnermostEHScope() const { return innermostEHScope; } + /// An unstable reference to a scope-stack depth. Invalidated by /// pushes but not pops. class iterator; @@ -233,6 +240,9 @@ class EHScopeStack { /// Returns an iterator pointing to the innermost EH scope. iterator begin() const; + /// Returns an iterator pointing to the outermost EH scope. + iterator end() const; + /// Create a stable reference to the top of the EH stack. The /// returned reference is valid until that scope is popped off the /// stack. diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp new file mode 100644 index 0000000000000..078447f844d9a --- /dev/null +++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp @@ -0,0 +1,44 @@ +// 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 -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +int division(); + +void calling_division_inside_try_block() { + try { + division(); + } catch (...) { + } +} + +// CIR: cir.scope { +// CIR: cir.try { +// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i +// CIR: cir.yield +// CIR: } catch all { +// CIR: cir.yield +// CIR: } +// CIR: } + +// OGCG: %[[EXN_OBJ_ADDR:.*]] = alloca ptr, align 8 +// OGCG: %[[EH_SELECTOR_ADDR:.*]] = alloca i32, align 4 +// OGCG: %[[CALL:.*]] = invoke noundef i32 @_Z8divisionv() +// OGCG: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]] +// OGCG: [[INVOKE_CONT]]: +// OGCG: br label %[[TRY_CONT:.*]] +// OGCG: [[LANDING_PAD]]: +// OGCG: %[[LP:.*]] = landingpad { ptr, i32 } +// OGCG: catch ptr null +// OGCG: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0 +// OGCG: store ptr %[[EXN_OBJ]], ptr %[[EXN_OBJ_ADDR]], align 8 +// OGCG: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1 +// OGCG: store i32 %[[EH_SELECTOR_VAL]], ptr %[[EH_SELECTOR_ADDR]], align 4 +// OGCG: br label %[[CATCH:.*]] +// OGCG: [[CATCH]]: +// OGCG: %[[EXN_OBJ:.*]] = load ptr, ptr %[[EXN_OBJ_ADDR]], align 8 +// OGCG: %[[CATCH_BEGIN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ]]) +// OGCG: call void @__cxa_end_catch() +// OGCG: br label %[[TRY_CONT]] +// OGCG: [[TRY_CONT]]: +// OGCG: ret void