Skip to content

Commit 51ba8bd

Browse files
committed
[CIR] Support Try catch with handler for specific type
1 parent c4ff1f3 commit 51ba8bd

File tree

7 files changed

+206
-18
lines changed

7 files changed

+206
-18
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
835835
//===----------------------------------------------------------------------===//
836836

837837
defvar CIR_YieldableScopes = [
838-
"ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
838+
"ArrayCtor", "ArrayDtor", "AwaitOp", "CallOp", "CaseOp", "DoWhileOp", "ForOp",
839839
"GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
840840
];
841841

@@ -3015,6 +3015,7 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
30153015

30163016
let results = (outs Optional<CIR_AnyType>:$result);
30173017
let arguments = commonArgs;
3018+
let regions = (region AnyRegion:$cleanup);
30183019

30193020
let builders = [
30203021
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
@@ -3024,6 +3025,8 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
30243025
$_state.addAttribute("callee", callee);
30253026
if (resType && !isa<VoidType>(resType))
30263027
$_state.addTypes(resType);
3028+
// Create region placeholder for potential cleanups.
3029+
$_state.addRegion();
30273030
}]>
30283031
];
30293032
}

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class CIRGenCXXABI {
161161
/// Loads the incoming C++ this pointer as it was passed by the caller.
162162
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);
163163

164+
virtual CatchTypeInfo
165+
getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
166+
QualType catchHandlerType) = 0;
164167
virtual CatchTypeInfo getCatchAllTypeInfo();
165168

166169
/// Get the implicit (second) parameter that comes after the "this" pointer,

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,9 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
503503
callOpWithExceptions =
504504
builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
505505

506+
cgf.callWithExceptionCtx = callOpWithExceptions;
506507
cgf.populateCatchHandlersIfRequired(tryOp);
508+
cgf.callWithExceptionCtx = nullptr;
507509
return callOpWithExceptions;
508510
}
509511

clang/lib/CIR/CodeGen/CIRGenException.cpp

Lines changed: 133 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,30 @@ void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
232232
assert(!cir::MissingFeatures::ehCleanupScope());
233233
}
234234

235+
void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup,
236+
cir::TryOp tryOp) {
237+
const EHPersonality &personality = EHPersonality::get(*this);
238+
// This can always be a call because we necessarily didn't find
239+
// anything on the EH stack which needs our help.
240+
const char *rethrowName = personality.catchallRethrowFn;
241+
if (rethrowName != nullptr && !isCleanup) {
242+
cgm.errorNYI("populateUnwindResumeBlock CatchallRethrowFn");
243+
return;
244+
}
245+
246+
unsigned regionsNum = tryOp->getNumRegions();
247+
mlir::Region *unwindRegion = &tryOp->getRegion(regionsNum - 1);
248+
mlir::Block *unwindResumeBlock = &unwindRegion->front();
249+
if (!unwindResumeBlock->empty())
250+
return;
251+
252+
// Emit cir.resume into the unwind region last block
253+
cir::CIRBaseBuilderTy::InsertPoint ip = builder.saveInsertionPoint();
254+
builder.setInsertionPointToStart(unwindResumeBlock);
255+
cir::ResumeOp::create(builder, tryOp.getLoc());
256+
builder.restoreInsertionPoint(ip);
257+
}
258+
235259
mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
236260
if (s.getTryBlock()->body_empty())
237261
return mlir::LogicalResult::success();
@@ -332,21 +356,88 @@ CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
332356
return mlir::success();
333357
}
334358

359+
/// Emit the structure of the dispatch block for the given catch scope.
360+
/// It is an invariant that the dispatch block already exists.
361+
static void emitCatchDispatchBlock(CIRGenFunction &cgf,
362+
EHCatchScope &catchScope, cir::TryOp tryOp) {
363+
if (EHPersonality::get(cgf).isWasmPersonality()) {
364+
cgf.cgm.errorNYI("emitCatchDispatchBlock: WasmPersonality");
365+
return;
366+
}
367+
368+
if (EHPersonality::get(cgf).usesFuncletPads()) {
369+
cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads");
370+
return;
371+
}
372+
373+
unsigned int numHandlers = catchScope.getNumHandlers();
374+
if (numHandlers == 1 && catchScope.getHandler(0).isCatchAll()) {
375+
return;
376+
}
377+
378+
// In traditional LLVM codegen, the right handler is selected (with
379+
// calls to eh_typeid_for) and the selector value is loaded. After that,
380+
// blocks get connected for later codegen. In CIR, these are all
381+
// implicit behaviors of cir.catch - not a lot of work to do.
382+
//
383+
// Test against each of the exception types we claim to catch.
384+
for (unsigned i = 0;; ++i) {
385+
assert(i < numHandlers && "ran off end of handlers!");
386+
const EHCatchScope::Handler &handler = catchScope.getHandler(i);
387+
388+
[[maybe_unused]] mlir::TypedAttr typeValue = handler.type.rtti;
389+
assert(handler.Type.Flags == 0 && "catch handler flags not supported");
390+
assert(typeValue && "fell into catch-all case!");
391+
392+
// Check for address space mismatch
393+
assert(!cir::MissingFeatures::addressSpace());
394+
395+
// If this is the last handler, we're at the end, and the next
396+
// block is the block for the enclosing EH scope. Make sure to call
397+
// populateEHCatchRegions for caching it.
398+
if (i + 1 == numHandlers) {
399+
cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp);
400+
return;
401+
}
402+
403+
// If the next handler is a catch-all, we're at the end, and the
404+
// next block is that handler.
405+
if (catchScope.getHandler(i + 1).isCatchAll())
406+
return;
407+
}
408+
}
409+
335410
void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
336411
bool isFnTryBlock) {
337412
unsigned numHandlers = s.getNumHandlers();
338413
EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
339414
for (unsigned i = 0; i != numHandlers; ++i) {
340415
const CXXCatchStmt *catchStmt = s.getHandler(i);
416+
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
341417
if (catchStmt->getExceptionDecl()) {
342-
cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
343-
return;
344-
}
418+
// FIXME: Dropping the reference type on the type into makes it
419+
// impossible to correctly implement catch-by-reference
420+
// semantics for pointers. Unfortunately, this is what all
421+
// existing compilers do, and it's not clear that the standard
422+
// personality routine is capable of doing this right. See C++ DR 388:
423+
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
424+
Qualifiers caughtTypeQuals;
425+
QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
426+
catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
427+
if (caughtType->isObjCObjectPointerType()) {
428+
cgm.errorNYI("enterCXXTryStmt: caughtType ObjCObjectPointerType");
429+
return;
430+
}
345431

346-
// No exception decl indicates '...', a catch-all.
347-
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
348-
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
349-
s.getHandler(i));
432+
CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
433+
getLoc(catchStmt->getSourceRange()), caughtType,
434+
catchStmt->getCaughtType());
435+
catchScope->setHandler(i, typeInfo, handler, catchStmt);
436+
} else {
437+
// No exception decl indicates '...', a catch-all.
438+
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
439+
s.getHandler(i));
440+
}
350441

351442
// Under async exceptions, catch(...) needs to catch HW exception too
352443
// Mark scope with SehTryBegin as a SEH __try scope
@@ -385,6 +476,9 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
385476
return;
386477
}
387478

479+
// Emit the structure of the EH dispatch for this catch.
480+
emitCatchDispatchBlock(*this, catchScope, tryOp);
481+
388482
// Copy the handler blocks off before we pop the EH stack. Emitting
389483
// the handlers might scribble on this memory.
390484
SmallVector<EHCatchScope::Handler> handlers(catchScope.begin(),
@@ -487,9 +581,11 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
487581
// with function local static initializers).
488582
mlir::ArrayAttr handlerTypesAttr = tryOp.getHandlerTypesAttr();
489583
if (!handlerTypesAttr || handlerTypesAttr.empty()) {
584+
// Accumulate all the handlers in scope.
490585
// Accumulate all the handlers in scope.
491586
bool hasCatchAll = false;
492-
llvm::SmallVector<mlir::Attribute, 4> handlerAttrs;
587+
llvm::SmallPtrSet<mlir::Attribute, 4> catchTypes;
588+
llvm::SmallVector<mlir::Attribute> handlerAttrs;
493589
for (EHScopeStack::iterator i = ehStack.begin(), e = ehStack.end(); i != e;
494590
++i) {
495591
switch (i->getKind()) {
@@ -522,8 +618,10 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
522618
break;
523619
}
524620

525-
cgm.errorNYI("emitLandingPad: non catch-all");
526-
return;
621+
// Check whether we already have a handler for this type.
622+
// If not, keep track to later add to catch op.
623+
if (catchTypes.insert(handler.type.rtti).second)
624+
handlerAttrs.push_back(handler.type.rtti);
527625
}
528626

529627
if (hasCatchAll)
@@ -532,9 +630,12 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
532630

533631
if (hasCatchAll) {
534632
handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
535-
} else {
536-
cgm.errorNYI("emitLandingPad: non catch-all");
537-
return;
633+
}
634+
635+
// If there's no catch_all, attach the unwind region. This needs to be the
636+
// last region in the TryOp catch list.
637+
if (!hasCatchAll) {
638+
handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
538639
}
539640

540641
// Add final array of clauses into TryOp.
@@ -559,6 +660,13 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
559660
return;
560661
}
561662

663+
// The dispatch block for the end of the scope chain is a block that
664+
// just resumes unwinding.
665+
if (scope == ehStack.stable_end()) {
666+
populateUnwindResumeBlock(/*isCleanup=*/true, tryOp);
667+
return;
668+
}
669+
562670
// Otherwise, we should look at the actual scope.
563671
EHScope &ehScope = *ehStack.find(scope);
564672
bool mayThrow = ehScope.mayThrow();
@@ -576,16 +684,25 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
576684
if (!mayThrow) {
577685
switch (ehScope.getKind()) {
578686
case EHScope::Catch: {
687+
mayThrow = true;
688+
579689
// LLVM does some optimization with branches here, CIR just keep track of
580690
// the corresponding calls.
581691
EHCatchScope &catchScope = cast<EHCatchScope>(ehScope);
582692
if (catchScope.getNumHandlers() == 1 &&
583693
catchScope.getHandler(0).isCatchAll()) {
584-
mayThrow = true;
585694
break;
586695
}
587-
cgm.errorNYI("getEHDispatchBlock: mayThrow non-catch all");
588-
return;
696+
697+
assert(callWithExceptionCtx && "expected call information");
698+
{
699+
mlir::OpBuilder::InsertionGuard guard(builder);
700+
assert(callWithExceptionCtx.getCleanup().empty() &&
701+
"one per call: expected empty region at this point");
702+
builder.createBlock(&callWithExceptionCtx.getCleanup());
703+
builder.createYield(callWithExceptionCtx.getLoc());
704+
}
705+
break;
589706
}
590707
case EHScope::Cleanup: {
591708
cgm.errorNYI("getEHDispatchBlock: mayThrow & cleanup");

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,8 @@ class CIRGenFunction : public CIRGenTypeCache {
974974
return false;
975975
}
976976

977+
cir::CallOp callWithExceptionCtx = nullptr;
978+
void populateUnwindResumeBlock(bool isCleanup, cir::TryOp tryOp);
977979
void populateEHCatchRegions(EHScopeStack::stable_iterator scope,
978980
cir::TryOp tryOp);
979981

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
121121

122122
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
123123
QualType ty) override;
124+
CatchTypeInfo
125+
getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
126+
QualType catchHandlerType) override {
127+
auto rtti = dyn_cast<cir::GlobalViewAttr>(getAddrOfRTTIDescriptor(loc, ty));
128+
assert(rtti && "expected GlobalViewAttr");
129+
return CatchTypeInfo{rtti, 0};
130+
}
124131

125132
bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
126133
return true;

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

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
int division();
77

8-
void calling_division_inside_try_block() {
8+
void call_function_inside_try_catch_all() {
99
try {
1010
division();
1111
} catch (...) {
@@ -43,3 +43,57 @@ void calling_division_inside_try_block() {
4343
// OGCG: br label %[[TRY_CONT]]
4444
// OGCG: [[TRY_CONT]]:
4545
// OGCG: ret void
46+
47+
void call_function_inside_try_catch_with_exception_type() {
48+
try {
49+
division();
50+
} catch (int e) {
51+
}
52+
}
53+
54+
// CIR: cir.scope {
55+
// CIR: cir.try {
56+
// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i
57+
// CIR: cir.yield
58+
// CIR: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
59+
// CIR: cir.yield
60+
// CIR: } unwind {
61+
// CIR: cir.resume
62+
// CIR: }
63+
// CIR: }
64+
65+
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
66+
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
67+
// OGCG: %[[E_ADDR:.*]] = alloca i32, align 4
68+
// OGCG: %[[CALL:.*]] = invoke noundef i32 @_Z8divisionv()
69+
// OGCG: to label %[[INVOKE_NORMAL:.*]] unwind label %[[INVOKE_UNWIND:.*]]
70+
// OGCG: [[INVOKE_NORMAL]]:
71+
// OGCG: br label %try.cont
72+
// OGCG: [[INVOKE_UNWIND]]:
73+
// OGCG: %[[LANDING_PAD:.*]] = landingpad { ptr, i32 }
74+
// OGCG: catch ptr @_ZTIi
75+
// OGCG: %[[EXCEPTION:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0
76+
// OGCG: store ptr %[[EXCEPTION]], ptr %[[EXCEPTION_ADDR]], align 8
77+
// OGCG: %[[EH_TYPE_ID:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1
78+
// OGCG: store i32 %[[EH_TYPE_ID]], ptr %[[EH_TYPE_ID_ADDR]], align 4
79+
// OGCG: br label %[[CATCH_DISPATCH:.*]]
80+
// OGCG: [[CATCH_DISPATCH]]:
81+
// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4
82+
// OGCG: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
83+
// OGCG: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[TMP_EH_TYPE_ID]], %[[EH_TYPE_ID]]
84+
// OGCG: br i1 %[[TYPE_ID_EQ]], label %[[CATCH_EXCEPTION:.*]], label %[[EH_RESUME:.*]]
85+
// OGCG: [[CATCH_EXCEPTION]]:
86+
// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8
87+
// OGCG: %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[TMP_EXCEPTION]])
88+
// OGCG: %[[TMP_BEGIN_CATCH:.*]] = load i32, ptr %[[BEGIN_CATCH]], align 4
89+
// OGCG: store i32 %[[TMP_BEGIN_CATCH]], ptr %[[E_ADDR]], align 4
90+
// OGCG: call void @__cxa_end_catch()
91+
// OGCG: br label %[[TRY_NORMA:.*]]
92+
// OGCG: [[TRY_NORMA]]:
93+
// OGCG: ret void
94+
// OGCG: [[EH_RESUME]]:
95+
// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8
96+
// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4
97+
// OGCG: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[TMP_EXCEPTION]], 0
98+
// OGCG: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[TMP_EH_TYPE_ID]], 1
99+
// OGCG: resume { ptr, i32 } %[[EXCEPTION_INFO]]

0 commit comments

Comments
 (0)