@@ -81,6 +81,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
8181 void emitRethrow (CIRGenFunction &cgf, bool isNoReturn) override ;
8282 void emitThrow (CIRGenFunction &cgf, const CXXThrowExpr *e) override ;
8383
84+ void emitBeginCatch (CIRGenFunction &cgf,
85+ const CXXCatchStmt *catchStmt) override ;
86+
8487 bool useThunkForDtorVariant (const CXXDestructorDecl *dtor,
8588 CXXDtorType dt) const override {
8689 // Itanium does not emit any destructor variant as an inline thunk.
@@ -2266,3 +2269,163 @@ Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &cgf,
22662269 CharUnits finalAlignment = baseAlignment.alignmentAtOffset (cookieSize);
22672270 return Address (finalPtr, newPtr.getElementType (), finalAlignment);
22682271}
2272+
2273+ namespace {
2274+ // / From traditional LLVM, useful info for LLVM lowering support:
2275+ // / A cleanup to call __cxa_end_catch. In many cases, the caught
2276+ // / exception type lets us state definitively that the thrown exception
2277+ // / type does not have a destructor. In particular:
2278+ // / - Catch-alls tell us nothing, so we have to conservatively
2279+ // / assume that the thrown exception might have a destructor.
2280+ // / - Catches by reference behave according to their base types.
2281+ // / - Catches of non-record types will only trigger for exceptions
2282+ // / of non-record types, which never have destructors.
2283+ // / - Catches of record types can trigger for arbitrary subclasses
2284+ // / of the caught type, so we have to assume the actual thrown
2285+ // / exception type might have a throwing destructor, even if the
2286+ // / caught type's destructor is trivial or nothrow.
2287+ struct CallEndCatch final : EHScopeStack::Cleanup {
2288+ CallEndCatch (bool mightThrow) : mightThrow(mightThrow) {}
2289+ bool mightThrow;
2290+
2291+ void emit (CIRGenFunction &cgf, Flags flags) override {
2292+ if (!mightThrow) {
2293+ // Traditional LLVM codegen would emit a call to __cxa_end_catch
2294+ // here. For CIR, just let it pass since the cleanup is going
2295+ // to be emitted on a later pass when lowering the catch region.
2296+ // CGF.EmitNounwindRuntimeCall(getEndCatchFn(CGF.CGM));
2297+ cir::YieldOp::create (cgf.getBuilder (), *cgf.currSrcLoc );
2298+ return ;
2299+ }
2300+
2301+ // Traditional LLVM codegen would emit a call to __cxa_end_catch
2302+ // here. For CIR, just let it pass since the cleanup is going
2303+ // to be emitted on a later pass when lowering the catch region.
2304+ // CGF.EmitRuntimeCallOrTryCall(getEndCatchFn(CGF.CGM));
2305+ if (!cgf.getBuilder ().getBlock ()->mightHaveTerminator ())
2306+ cir::YieldOp::create (cgf.getBuilder (), *cgf.currSrcLoc );
2307+ }
2308+ };
2309+ } // namespace
2310+
2311+ static mlir::Value callBeginCatch (CIRGenFunction &cgf, mlir::Type paramTy,
2312+ bool endMightThrow) {
2313+
2314+ auto catchParam = cir::CatchParamOp::create (
2315+ cgf.getBuilder (), cgf.getBuilder ().getUnknownLoc (), paramTy);
2316+
2317+ cgf.ehStack .pushCleanup <CallEndCatch>(
2318+ NormalAndEHCleanup,
2319+ endMightThrow && !cgf.cgm .getLangOpts ().AssumeNothrowExceptionDtor );
2320+
2321+ return catchParam.getParam ();
2322+ }
2323+
2324+ // / A "special initializer" callback for initializing a catch
2325+ // / parameter during catch initialization.
2326+ static void initCatchParam (CIRGenFunction &cgf, const VarDecl &catchParam,
2327+ Address paramAddr, SourceLocation loc) {
2328+ CanQualType catchType =
2329+ cgf.cgm .getASTContext ().getCanonicalType (catchParam.getType ());
2330+ // If we're catching by reference, we can just cast the object
2331+ // pointer to the appropriate pointer.
2332+ if (isa<ReferenceType>(catchType)) {
2333+ cgf.cgm .errorNYI (loc, " initCatchParam: ReferenceType" );
2334+ return ;
2335+ }
2336+
2337+ // Scalars and complexes.
2338+ cir::TypeEvaluationKind tek = cgf.getEvaluationKind (catchType);
2339+ if (tek != cir::TEK_Aggregate) {
2340+ // Notes for LLVM lowering:
2341+ // If the catch type is a pointer type, __cxa_begin_catch returns
2342+ // the pointer by value.
2343+ if (catchType->hasPointerRepresentation ()) {
2344+ cgf.cgm .errorNYI (loc, " initCatchParam: hasPointerRepresentation" );
2345+ return ;
2346+ }
2347+
2348+ mlir::Type cirCatchTy = cgf.convertTypeForMem (catchType);
2349+ mlir::Value catchParam =
2350+ callBeginCatch (cgf, cgf.getBuilder ().getPointerTo (cirCatchTy), false );
2351+ LValue srcLV = cgf.makeNaturalAlignAddrLValue (catchParam, catchType);
2352+ LValue destLV = cgf.makeAddrLValue (paramAddr, catchType);
2353+ switch (tek) {
2354+ case cir::TEK_Complex: {
2355+ cgf.cgm .errorNYI (loc, " initCatchParam: cir::TEK_Complex" );
2356+ return ;
2357+ }
2358+ case cir::TEK_Scalar: {
2359+ auto exnLoad = cgf.emitLoadOfScalar (srcLV, loc);
2360+ cgf.emitStoreOfScalar (exnLoad, destLV, /* isInit=*/ true );
2361+ return ;
2362+ }
2363+ case cir::TEK_Aggregate:
2364+ llvm_unreachable (" evaluation kind filtered out!" );
2365+ }
2366+
2367+ // Otherwise, it returns a pointer into the exception object.
2368+ llvm_unreachable (" bad evaluation kind" );
2369+ }
2370+
2371+ cgf.cgm .errorNYI (loc, " initCatchParam: cir::TEK_Aggregate" );
2372+ }
2373+
2374+ // / Begins a catch statement by initializing the catch variable and
2375+ // / calling __cxa_begin_catch.
2376+ void CIRGenItaniumCXXABI::emitBeginCatch (CIRGenFunction &cgf,
2377+ const CXXCatchStmt *catchStmt) {
2378+ // We have to be very careful with the ordering of cleanups here:
2379+ // C++ [except.throw]p4:
2380+ // The destruction [of the exception temporary] occurs
2381+ // immediately after the destruction of the object declared in
2382+ // the exception-declaration in the handler.
2383+ //
2384+ // So the precise ordering is:
2385+ // 1. Construct catch variable.
2386+ // 2. __cxa_begin_catch
2387+ // 3. Enter __cxa_end_catch cleanup
2388+ // 4. Enter dtor cleanup
2389+ //
2390+ // We do this by using a slightly abnormal initialization process.
2391+ // Delegation sequence:
2392+ // - ExitCXXTryStmt opens a RunCleanupsScope
2393+ // - EmitAutoVarAlloca creates the variable and debug info
2394+ // - InitCatchParam initializes the variable from the exception
2395+ // - CallBeginCatch calls __cxa_begin_catch
2396+ // - CallBeginCatch enters the __cxa_end_catch cleanup
2397+ // - EmitAutoVarCleanups enters the variable destructor cleanup
2398+ // - EmitCXXTryStmt emits the code for the catch body
2399+ // - EmitCXXTryStmt close the RunCleanupsScope
2400+
2401+ VarDecl *catchParam = catchStmt->getExceptionDecl ();
2402+ if (!catchParam) {
2403+ callBeginCatch (cgf, cgf.getBuilder ().getVoidPtrTy (),
2404+ /* endMightThrow=*/ true );
2405+ return ;
2406+ }
2407+
2408+ auto getCatchParamAllocaIP = [&]() {
2409+ auto currIns = cgf.getBuilder ().saveInsertionPoint ();
2410+ mlir::Operation *currParent = currIns.getBlock ()->getParentOp ();
2411+
2412+ mlir::Block *insertBlock = nullptr ;
2413+ if (auto scopeOp = currParent->getParentOfType <cir::ScopeOp>()) {
2414+ insertBlock = &scopeOp.getScopeRegion ().getBlocks ().back ();
2415+ } else if (auto fnOp = currParent->getParentOfType <cir::FuncOp>()) {
2416+ insertBlock = &fnOp.getRegion ().getBlocks ().back ();
2417+ } else {
2418+ llvm_unreachable (" unknown outermost scope-like parent" );
2419+ }
2420+ return cgf.getBuilder ().getBestAllocaInsertPoint (insertBlock);
2421+ };
2422+
2423+ // Emit the local. Make sure the alloca's superseed the current scope, since
2424+ // these are going to be consumed by `cir.catch`, which is not within the
2425+ // current scope.
2426+ CIRGenFunction::AutoVarEmission var =
2427+ cgf.emitAutoVarAlloca (*catchParam, getCatchParamAllocaIP ());
2428+ initCatchParam (cgf, *catchParam, var.getObjectAddress (cgf),
2429+ catchStmt->getBeginLoc ());
2430+ cgf.emitAutoVarCleanups (var);
2431+ }
0 commit comments