Skip to content

Commit b74f4e3

Browse files
committed
[CIR] Emit CatchParamOp in the catch region
1 parent 68fea00 commit b74f4e3

File tree

5 files changed

+170
-1
lines changed

5 files changed

+170
-1
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class CIRGenCXXABI {
126126

127127
virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0;
128128

129+
virtual void emitBeginCatch(CIRGenFunction &cgf,
130+
const CXXCatchStmt *catchStmt) = 0;
131+
129132
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
130133
QualType ty) = 0;
131134

clang/lib/CIR/CodeGen/CIRGenException.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,8 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
419419
RunCleanupsScope catchScope(*this);
420420

421421
// Initialize the catch variable and set up the cleanups.
422-
assert(!cir::MissingFeatures::catchParamOp());
422+
SaveAndRestore restoreCurrentFuncletPad(currentFuncletPad);
423+
cgm.getCXXABI().emitBeginCatch(*this, catchStmt);
423424

424425
// Emit the PGO counter increment.
425426
assert(!cir::MissingFeatures::incrementProfileCounter());

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ class CIRGenFunction : public CIRGenTypeCache {
12311231
};
12321232

12331233
LexicalScope *curLexScope = nullptr;
1234+
mlir::Operation *currentFuncletPad = nullptr;
12341235

12351236
typedef void Destroyer(CIRGenFunction &cgf, Address addr, QualType ty);
12361237

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

clang/test/CIR/CodeGen/try-catch-tmp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ void calling_division_inside_try_block() {
1717
// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i
1818
// CIR: cir.yield
1919
// CIR: } catch all {
20+
// CIR: %[[CATCH_PARAM:.*]] = cir.catch_param : !cir.ptr<!void>
2021
// CIR: cir.yield
2122
// CIR: }
2223
// CIR: }

0 commit comments

Comments
 (0)