-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[CIR] Support Try catch with handler for specific type #171042
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AmrDeveloper
wants to merge
3
commits into
llvm:main
Choose a base branch
from
AmrDeveloper:cir_catch_with_exception_decl
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
[CIR] Support Try catch with handler for specific type #171042
AmrDeveloper
wants to merge
3
commits into
llvm:main
from
AmrDeveloper:cir_catch_with_exception_decl
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Member
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Amr Hesham (AmrDeveloper) ChangesAdd support for try-catch with a handler for a specific exception type Issue #154992 Full diff: https://github.com/llvm/llvm-project/pull/171042.diff 7 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index fcc7585cf81a5..710a91320a155 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -802,7 +802,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
+ "ArrayCtor", "ArrayDtor", "AwaitOp", "CallOp", "CaseOp", "DoWhileOp", "ForOp",
"GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
];
@@ -2956,6 +2956,7 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let results = (outs Optional<CIR_AnyType>:$result);
let arguments = commonArgs;
+ let regions = (region AnyRegion:$cleanup);
let builders = [
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
@@ -2965,6 +2966,8 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
$_state.addAttribute("callee", callee);
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);
+ // Create region placeholder for potential cleanups.
+ $_state.addRegion();
}]>
];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 57b1a1f20aa17..c341a6139cea5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -158,6 +158,9 @@ class CIRGenCXXABI {
/// Loads the incoming C++ this pointer as it was passed by the caller.
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);
+ virtual CatchTypeInfo
+ getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
+ QualType catchHandlerType) = 0;
virtual CatchTypeInfo getCatchAllTypeInfo();
/// Get the implicit (second) parameter that comes after the "this" pointer,
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 17f0c6dbab35c..3ef443962aaa7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -503,7 +503,9 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
callOpWithExceptions =
builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
+ cgf.callWithExceptionCtx = callOpWithExceptions;
cgf.populateCatchHandlersIfRequired(tryOp);
+ cgf.callWithExceptionCtx = nullptr;
return callOpWithExceptions;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 375828421eb1b..d5e10ccaef456 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -232,6 +232,30 @@ void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
assert(!cir::MissingFeatures::ehCleanupScope());
}
+void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup,
+ cir::TryOp tryOp) {
+ const EHPersonality &personality = EHPersonality::get(*this);
+ // This can always be a call because we necessarily didn't find
+ // anything on the EH stack which needs our help.
+ const char *rethrowName = personality.catchallRethrowFn;
+ if (rethrowName != nullptr && !isCleanup) {
+ cgm.errorNYI("populateUnwindResumeBlock CatchallRethrowFn");
+ return;
+ }
+
+ unsigned regionsNum = tryOp->getNumRegions();
+ mlir::Region *unwindRegion = &tryOp->getRegion(regionsNum - 1);
+ mlir::Block *unwindResumeBlock = &unwindRegion->front();
+ if (!unwindResumeBlock->empty())
+ return;
+
+ // Emit cir.resume into the unwind region last block
+ cir::CIRBaseBuilderTy::InsertPoint ip = builder.saveInsertionPoint();
+ builder.setInsertionPointToStart(unwindResumeBlock);
+ cir::ResumeOp::create(builder, tryOp.getLoc());
+ builder.restoreInsertionPoint(ip);
+}
+
mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
if (s.getTryBlock()->body_empty())
return mlir::LogicalResult::success();
@@ -332,21 +356,88 @@ CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
return mlir::success();
}
+/// 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: WasmPersonality");
+ return;
+ }
+
+ if (EHPersonality::get(cgf).usesFuncletPads()) {
+ cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads");
+ return;
+ }
+
+ unsigned int numHandlers = catchScope.getNumHandlers();
+ if (numHandlers == 1 && catchScope.getHandler(0).isCatchAll()) {
+ return;
+ }
+
+ // In traditional LLVM codegen, the right handler is selected (with
+ // calls to eh_typeid_for) and the selector value is loaded. After that,
+ // blocks get connected for later codegen. In CIR, these are all
+ // implicit behaviors of cir.catch - not a lot of work to do.
+ //
+ // Test against each of the exception types we claim to catch.
+ for (unsigned i = 0;; ++i) {
+ assert(i < numHandlers && "ran off end of handlers!");
+ const EHCatchScope::Handler &handler = catchScope.getHandler(i);
+
+ [[maybe_unused]] mlir::TypedAttr typeValue = handler.type.rtti;
+ assert(handler.Type.Flags == 0 && "catch handler flags not supported");
+ assert(typeValue && "fell into catch-all case!");
+
+ // Check for address space mismatch
+ assert(!cir::MissingFeatures::addressSpace());
+
+ // If this is the last handler, we're at the end, and the next
+ // block is the block for the enclosing EH scope. Make sure to call
+ // populateEHCatchRegions for caching it.
+ if (i + 1 == numHandlers) {
+ cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp);
+ return;
+ }
+
+ // If the next handler is a catch-all, we're at the end, and the
+ // next block is that handler.
+ if (catchScope.getHandler(i + 1).isCatchAll())
+ return;
+ }
+}
+
void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
bool isFnTryBlock) {
unsigned numHandlers = s.getNumHandlers();
EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
for (unsigned i = 0; i != numHandlers; ++i) {
const CXXCatchStmt *catchStmt = s.getHandler(i);
+ mlir::Region *handler = &tryOp.getHandlerRegions()[i];
if (catchStmt->getExceptionDecl()) {
- cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
- return;
- }
+ // FIXME: Dropping the reference type on the type into makes it
+ // impossible to correctly implement catch-by-reference
+ // semantics for pointers. Unfortunately, this is what all
+ // existing compilers do, and it's not clear that the standard
+ // personality routine is capable of doing this right. See C++ DR 388:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
+ Qualifiers caughtTypeQuals;
+ QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
+ catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
+ if (caughtType->isObjCObjectPointerType()) {
+ cgm.errorNYI("enterCXXTryStmt: caughtType ObjCObjectPointerType");
+ return;
+ }
- // No exception decl indicates '...', a catch-all.
- mlir::Region *handler = &tryOp.getHandlerRegions()[i];
- catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
- s.getHandler(i));
+ CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
+ getLoc(catchStmt->getSourceRange()), caughtType,
+ catchStmt->getCaughtType());
+ catchScope->setHandler(i, typeInfo, handler, catchStmt);
+ } else {
+ // No exception decl indicates '...', a catch-all.
+ 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
@@ -385,6 +476,9 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
return;
}
+ // 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<EHCatchScope::Handler> handlers(catchScope.begin(),
@@ -486,9 +580,11 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
// with function local static initializers).
mlir::ArrayAttr handlerTypesAttr = tryOp.getHandlerTypesAttr();
if (!handlerTypesAttr || handlerTypesAttr.empty()) {
+ // Accumulate all the handlers in scope.
// Accumulate all the handlers in scope.
bool hasCatchAll = false;
- llvm::SmallVector<mlir::Attribute, 4> handlerAttrs;
+ llvm::SmallPtrSet<mlir::Attribute, 4> catchTypes;
+ llvm::SmallVector<mlir::Attribute> handlerAttrs;
for (EHScopeStack::iterator i = ehStack.begin(), e = ehStack.end(); i != e;
++i) {
switch (i->getKind()) {
@@ -521,8 +617,10 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
break;
}
- cgm.errorNYI("emitLandingPad: non catch-all");
- return;
+ // Check whether we already have a handler for this type.
+ // If not, keep track to later add to catch op.
+ if (catchTypes.insert(handler.type.rtti).second)
+ handlerAttrs.push_back(handler.type.rtti);
}
if (hasCatchAll)
@@ -531,9 +629,12 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
if (hasCatchAll) {
handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
- } else {
- cgm.errorNYI("emitLandingPad: non catch-all");
- return;
+ }
+
+ // If there's no catch_all, attach the unwind region. This needs to be the
+ // last region in the TryOp catch list.
+ if (!hasCatchAll) {
+ handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
}
// Add final array of clauses into TryOp.
@@ -558,6 +659,13 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
return;
}
+ // The dispatch block for the end of the scope chain is a block that
+ // just resumes unwinding.
+ if (scope == ehStack.stable_end()) {
+ populateUnwindResumeBlock(/*isCleanup=*/true, tryOp);
+ return;
+ }
+
// Otherwise, we should look at the actual scope.
EHScope &ehScope = *ehStack.find(scope);
bool mayThrow = ehScope.mayThrow();
@@ -575,16 +683,25 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
if (!mayThrow) {
switch (ehScope.getKind()) {
case EHScope::Catch: {
+ mayThrow = true;
+
// LLVM does some optimization with branches here, CIR just keep track of
// the corresponding calls.
EHCatchScope &catchScope = cast<EHCatchScope>(ehScope);
if (catchScope.getNumHandlers() == 1 &&
catchScope.getHandler(0).isCatchAll()) {
- mayThrow = true;
break;
}
- cgm.errorNYI("getEHDispatchBlock: mayThrow non-catch all");
- return;
+
+ assert(callWithExceptionCtx && "expected call information");
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ assert(callWithExceptionCtx.getCleanup().empty() &&
+ "one per call: expected empty region at this point");
+ builder.createBlock(&callWithExceptionCtx.getCleanup());
+ builder.createYield(callWithExceptionCtx.getLoc());
+ }
+ break;
}
case EHScope::Cleanup: {
cgm.errorNYI("getEHDispatchBlock: mayThrow & cleanup");
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 0df812bcfb94e..48bfcddc09009 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -969,6 +969,8 @@ class CIRGenFunction : public CIRGenTypeCache {
return false;
}
+ cir::CallOp callWithExceptionCtx = nullptr;
+ void populateUnwindResumeBlock(bool isCleanup, cir::TryOp tryOp);
void populateEHCatchRegions(EHScopeStack::stable_iterator scope,
cir::TryOp tryOp);
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 7e145f2c57ce6..2e51130e271f5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -118,6 +118,13 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType ty) override;
+ CatchTypeInfo
+ getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
+ QualType catchHandlerType) override {
+ auto rtti = dyn_cast<cir::GlobalViewAttr>(getAddrOfRTTIDescriptor(loc, ty));
+ assert(rtti && "expected GlobalViewAttr");
+ return CatchTypeInfo{rtti, 0};
+ }
bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
return true;
diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
index 078447f844d9a..5f57af9876615 100644
--- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp
+++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
@@ -5,7 +5,7 @@
int division();
-void calling_division_inside_try_block() {
+void call_function_inside_try_catch_all() {
try {
division();
} catch (...) {
@@ -42,3 +42,57 @@ void calling_division_inside_try_block() {
// OGCG: br label %[[TRY_CONT]]
// OGCG: [[TRY_CONT]]:
// OGCG: ret void
+
+void call_function_inside_try_catch_with_exception_type() {
+ try {
+ division();
+ } catch (int e) {
+ }
+}
+
+// CIR: cir.scope {
+// CIR: cir.try {
+// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i
+// CIR: cir.yield
+// CIR: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
+// CIR: cir.yield
+// CIR: } unwind {
+// CIR: cir.resume
+// CIR: }
+// CIR: }
+
+// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
+// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
+// OGCG: %[[E_ADDR:.*]] = alloca i32, align 4
+// OGCG: %[[CALL:.*]] = invoke noundef i32 @_Z8divisionv()
+// OGCG: to label %[[INVOKE_NORMAL:.*]] unwind label %[[INVOKE_UNWIND:.*]]
+// OGCG: [[INVOKE_NORMAL]]:
+// OGCG: br label %try.cont
+// OGCG: [[INVOKE_UNWIND]]:
+// OGCG: %[[LANDING_PAD:.*]] = landingpad { ptr, i32 }
+// OGCG: catch ptr @_ZTIi
+// OGCG: %[[EXCEPTION:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0
+// OGCG: store ptr %[[EXCEPTION]], ptr %[[EXCEPTION_ADDR]], align 8
+// OGCG: %[[EH_TYPE_ID:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1
+// OGCG: store i32 %[[EH_TYPE_ID]], ptr %[[EH_TYPE_ID_ADDR]], align 4
+// OGCG: br label %[[CATCH_DISPATCH:.*]]
+// OGCG: [[CATCH_DISPATCH]]:
+// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4
+// OGCG: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
+// OGCG: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[TMP_EH_TYPE_ID]], %[[EH_TYPE_ID]]
+// OGCG: br i1 %[[TYPE_ID_EQ]], label %[[CATCH_EXCEPTION:.*]], label %[[EH_RESUME:.*]]
+// OGCG: [[CATCH_EXCEPTION]]:
+// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8
+// OGCG: %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[TMP_EXCEPTION]])
+// OGCG: %[[TMP_BEGIN_CATCH:.*]] = load i32, ptr %[[BEGIN_CATCH]], align 4
+// OGCG: store i32 %[[TMP_BEGIN_CATCH]], ptr %[[E_ADDR]], align 4
+// OGCG: call void @__cxa_end_catch()
+// OGCG: br label %[[TRY_NORMA:.*]]
+// OGCG: [[TRY_NORMA]]:
+// OGCG: ret void
+// OGCG: [[EH_RESUME]]:
+// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8
+// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4
+// OGCG: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[TMP_EXCEPTION]], 0
+// OGCG: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[TMP_EH_TYPE_ID]], 1
+// OGCG: resume { ptr, i32 } %[[EXCEPTION_INFO]]
|
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Add support for try-catch with a handler for a specific exception type
Issue #154992