diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 6c7e3d055456a..737ceac80635b 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -389,25 +389,6 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createCallOp(loc, callee, cir::VoidType(), operands, attrs); } - cir::CallOp createTryCallOp( - mlir::Location loc, mlir::SymbolRefAttr callee = mlir::SymbolRefAttr(), - mlir::Type returnType = cir::VoidType(), - mlir::ValueRange operands = mlir::ValueRange(), - [[maybe_unused]] cir::SideEffect sideEffect = cir::SideEffect::All) { - assert(!cir::MissingFeatures::opCallCallConv()); - assert(!cir::MissingFeatures::opCallSideEffect()); - return createCallOp(loc, callee, returnType, operands); - } - - cir::CallOp createTryCallOp( - mlir::Location loc, cir::FuncOp callee, mlir::ValueRange operands, - [[maybe_unused]] cir::SideEffect sideEffect = cir::SideEffect::All) { - assert(!cir::MissingFeatures::opCallCallConv()); - assert(!cir::MissingFeatures::opCallSideEffect()); - return createTryCallOp(loc, mlir::SymbolRefAttr::get(callee), - callee.getFunctionType().getReturnType(), operands); - } - //===--------------------------------------------------------------------===// // Cast/Conversion Operators //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 567c79a27c07b..d93ee2675b366 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -94,6 +94,7 @@ struct MissingFeatures { static bool opFuncNoReturn() { return false; } static bool setFunctionAttributes() { return false; } static bool setLLVMFunctionFEnvAttributes() { return false; } + static bool setFunctionPersonality() { return false; } // CallOp handling static bool opCallAggregateArgs() { return false; } @@ -334,6 +335,9 @@ struct MissingFeatures { static bool vtableRelativeLayout() { return false; } static bool weakRefReference() { return false; } static bool writebacks() { return false; } + static bool msvcCXXPersonality() { return false; } + static bool functionUsesSEHTry() { return false; } + static bool nothrowAttr() { return false; } // Missing types static bool dataMemberType() { return false; } @@ -349,7 +353,6 @@ struct MissingFeatures { static bool awaitOp() { return false; } static bool callOp() { return false; } static bool ifOp() { return false; } - static bool invokeOp() { return false; } static bool labelOp() { return false; } static bool ptrDiffOp() { return false; } static bool llvmLoweringPtrDiffConsidersPointee() { return false; } @@ -359,6 +362,7 @@ struct MissingFeatures { static bool tryOp() { return false; } static bool vecTernaryOp() { return false; } static bool zextOp() { return false; } + static bool catchParamOp() { return false; } // Future CIR attributes static bool optInfoAttr() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 50d4c035d30a1..17f0c6dbab35c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -465,12 +465,47 @@ 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 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"); @@ -601,8 +636,6 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, assert(!cir::MissingFeatures::opCallAttrs()); cgm.constructAttributeList(callee.getAbstractInfo(), attrs); - assert(!cir::MissingFeatures::invokeOp()); - cir::FuncType indirectFuncTy; mlir::Value indirectFuncVal; cir::FuncOp directFuncOp; @@ -628,10 +661,17 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, indirectFuncVal = calleePtr->getResult(0); } + assert(!cir::MissingFeatures::msvcCXXPersonality()); + assert(!cir::MissingFeatures::functionUsesSEHTry()); + assert(!cir::MissingFeatures::nothrowAttr()); + + bool cannotThrow = attrs.getNamed("nothrow").has_value(); + bool isInvoke = !cannotThrow && isCatchOrCleanupRequired(); + 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 437db306f3369..33f2de9137514 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -188,9 +188,22 @@ void EHScopeStack::popCleanup() { } } +bool EHScopeStack::requiresCatchOrCleanup() const { + for (stable_iterator si = getInnermostEHScope(); si != stable_end();) { + if (auto *cleanup = dyn_cast(&*find(si))) { + if (cleanup->isLifetimeMarker()) { + // Skip lifetime markers and continue from the enclosing EH scope + assert(!cir::MissingFeatures::emitLifetimeMarkers()); + continue; + } + } + 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, innermostEHScope); innermostEHScope = stable_begin(); diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h index a035d792ef6d1..9cc2556c05967 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h @@ -18,6 +18,7 @@ #include "CIRGenModule.h" #include "EHScopeStack.h" #include "mlir/IR/Value.h" +#include "clang/AST/StmtCXX.h" namespace clang::CIRGen { @@ -38,6 +39,8 @@ class EHScope { }; enum { NumCommonBits = 3 }; + bool scopeMayThrow; + protected: class CatchBitFields { friend class EHCatchScope; @@ -92,10 +95,11 @@ 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 scopeMayThrow; } + void setMayThrow(bool mayThrow) { scopeMayThrow = mayThrow; } + EHScopeStack::stable_iterator getEnclosingEHScope() const { return enclosingEHScope; } @@ -121,6 +125,9 @@ class EHCatchScope : public EHScope { /// The catch handler for this type. mlir::Region *region; + /// The catch handler stmt. + const CXXCatchStmt *stmt; + bool isCatchAll() const { return type.rtti == nullptr; } }; @@ -147,10 +154,13 @@ class EHCatchScope : public EHScope { unsigned getNumHandlers() const { return catchBits.numHandlers; } - void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region) { + void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region, + const CXXCatchStmt *stmt) { assert(i < getNumHandlers()); - getHandlers()[i].type = type; - getHandlers()[i].region = region; + Handler *handler = &getHandlers()[i]; + handler->type = type; + handler->region = region; + handler->stmt = stmt; } const Handler &getHandler(unsigned i) const { @@ -231,6 +241,8 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope bool isActive() const { return cleanupBits.isActive; } void setActive(bool isActive) { cleanupBits.isActive = isActive; } + bool isLifetimeMarker() const { return cleanupBits.isLifetimeMarker; } + unsigned getFixupDepth() const { return fixupDepth; } EHScopeStack::stable_iterator getEnclosingNormalCleanup() const { return enclosingNormal; diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 67f46ffde8fda..765f593fe0184 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -14,6 +14,8 @@ #include "CIRGenFunction.h" #include "clang/AST/StmtVisitor.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace clang::CIRGen; @@ -343,7 +345,8 @@ void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp, // No exception decl indicates '...', a catch-all. mlir::Region *handler = &tryOp.getHandlerRegions()[i]; - catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler); + 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 @@ -382,5 +385,268 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) { return; } - cgm.errorNYI("exitCXXTryStmt: Required catch"); + // 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 (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()); + + // Enter a cleanup scope, including the catch variable and the + // end-catch. + RunCleanupsScope catchScope(*this); + + // Initialize the catch variable and set up the cleanups. + assert(!cir::MissingFeatures::catchParamOp()); + + // 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"); + + assert(!cir::MissingFeatures::catchParamOp()); + 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()); +} + +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::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; + } // end switch + + EHCatchScope &catchScope = cast(*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; + } + + cgm.errorNYI("emitLandingPad: non catch-all"); + return; + } + + if (hasCatchAll) + break; + } + + 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, + // 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; + } + + // 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); +} + +// in classic codegen this function is mapping to `isInvokeDest` previously and +// currently it's mapping to the conditions that performs early returns in +// `getInvokeDestImpl`, in CIR we need the condition to know if the EH scope may +// throw exception or now. +bool CIRGenFunction::isCatchOrCleanupRequired() { + // 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) { + if (!lo.Borland && !lo.MicrosoftExt) + return false; + 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 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()); + + assert(!cir::MissingFeatures::setFunctionPersonality()); + + // CIR does not cache landing pads. + const EHPersonality &personality = EHPersonality::get(*this); + if (personality.usesFuncletPads()) { + cgm.errorNYI("getInvokeDestImpl: usesFuncletPads"); + } else { + populateCatchHandlers(tryOp); + } } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 866fda3166f41..2739469d7202e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -412,6 +412,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 00f289bcd1bb2..2dddf26981105 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -929,10 +929,16 @@ class CIRGenFunction : public CIRGenTypeCache { return false; } + void populateEHCatchRegions(EHScopeStack::stable_iterator scope, + cir::TryOp tryOp); + /// 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. void popCleanupBlocks(EHScopeStack::stable_iterator oldCleanupStackDepth); @@ -1076,7 +1082,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; } @@ -1630,6 +1636,8 @@ 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 9005b0106b2a4..6455bd8a55c83 100644 --- a/clang/lib/CIR/CodeGen/EHScopeStack.h +++ b/clang/lib/CIR/CodeGen/EHScopeStack.h @@ -217,6 +217,8 @@ class EHScopeStack { /// Determines whether the exception-scopes stack is empty. bool empty() const { return startOfData == endOfBuffer; } + bool requiresCatchOrCleanup() const; + /// Determines whether there are any normal cleanups on the stack. bool hasNormalCleanups() const { return innermostNormalCleanup != stable_end(); 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