Skip to content

Commit 18b38cf

Browse files
authored
[CIR] Support Try catch with handler for specific type (#171042)
Add support for try-catch with a handler for a specific exception type Issue #154992
1 parent 13f7b30 commit 18b38cf

File tree

7 files changed

+316
-25
lines changed

7 files changed

+316
-25
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,6 +3032,8 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
30323032
let results = (outs Optional<CIR_AnyType>:$result);
30333033
let arguments = commonArgs;
30343034

3035+
let skipDefaultBuilders = 1;
3036+
30353037
let builders = [
30363038
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
30373039
"mlir::ValueRange":$operands), [{

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ struct MissingFeatures {
263263
static bool ehCleanupScope() { return false; }
264264
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
265265
static bool ehCleanupBranchFixups() { return false; }
266+
static bool ehScopeFilter() { return false; }
266267
static bool ehstackBranches() { return false; }
267268
static bool emitBranchThroughCleanup() { return false; }
268269
static bool emitCheckedInBoundsGEP() { return false; }
@@ -376,7 +377,6 @@ struct MissingFeatures {
376377
static bool tryOp() { return false; }
377378
static bool vecTernaryOp() { return false; }
378379
static bool zextOp() { return false; }
379-
static bool catchParamOp() { return false; }
380380

381381
// Future CIR attributes
382382
static bool optInfoAttr() { return false; }

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/CIRGenException.cpp

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,29 @@ void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
244244
assert(!cir::MissingFeatures::ehCleanupScope());
245245
}
246246

247+
void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup,
248+
cir::TryOp tryOp) {
249+
const EHPersonality &personality = EHPersonality::get(*this);
250+
// This can always be a call because we necessarily didn't find
251+
// anything on the EH stack which needs our help.
252+
const char *rethrowName = personality.catchallRethrowFn;
253+
if (rethrowName != nullptr && !isCleanup) {
254+
cgm.errorNYI("populateUnwindResumeBlock CatchallRethrowFn");
255+
return;
256+
}
257+
258+
unsigned regionsNum = tryOp->getNumRegions();
259+
mlir::Region *unwindRegion = &tryOp->getRegion(regionsNum - 1);
260+
mlir::Block *unwindResumeBlock = &unwindRegion->front();
261+
if (!unwindResumeBlock->empty())
262+
return;
263+
264+
// Emit cir.resume into the unwind region last block
265+
mlir::OpBuilder::InsertionGuard guard(builder);
266+
builder.setInsertionPointToStart(unwindResumeBlock);
267+
cir::ResumeOp::create(builder, tryOp.getLoc());
268+
}
269+
247270
mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
248271
if (s.getTryBlock()->body_empty())
249272
return mlir::LogicalResult::success();
@@ -344,21 +367,63 @@ CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
344367
return mlir::success();
345368
}
346369

370+
/// Emit the structure of the dispatch block for the given catch scope.
371+
/// It is an invariant that the dispatch block already exists.
372+
static void emitCatchDispatchBlock(CIRGenFunction &cgf,
373+
EHCatchScope &catchScope, cir::TryOp tryOp) {
374+
if (EHPersonality::get(cgf).isWasmPersonality()) {
375+
cgf.cgm.errorNYI("emitCatchDispatchBlock: WasmPersonality");
376+
return;
377+
}
378+
379+
if (EHPersonality::get(cgf).usesFuncletPads()) {
380+
cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads");
381+
return;
382+
}
383+
384+
assert(std::find_if(catchScope.begin(), catchScope.end(),
385+
[](const auto &handler) {
386+
return !handler.type.rtti && handler.type.flags != 0;
387+
}) == catchScope.end() &&
388+
"catch handler without type value or with not supported flags");
389+
390+
// There was no catch all handler, populate th EH regions for the
391+
// enclosing scope.
392+
if (!std::prev(catchScope.end())->isCatchAll())
393+
cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp);
394+
}
395+
347396
void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
348397
bool isFnTryBlock) {
349398
unsigned numHandlers = s.getNumHandlers();
350399
EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
351400
for (unsigned i = 0; i != numHandlers; ++i) {
352401
const CXXCatchStmt *catchStmt = s.getHandler(i);
402+
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
353403
if (catchStmt->getExceptionDecl()) {
354-
cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
355-
return;
356-
}
404+
// FIXME: Dropping the reference type on the type into makes it
405+
// impossible to correctly implement catch-by-reference
406+
// semantics for pointers. Unfortunately, this is what all
407+
// existing compilers do, and it's not clear that the standard
408+
// personality routine is capable of doing this right. See C++ DR 388:
409+
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
410+
Qualifiers caughtTypeQuals;
411+
QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
412+
catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
413+
if (caughtType->isObjCObjectPointerType()) {
414+
cgm.errorNYI("enterCXXTryStmt: caughtType ObjCObjectPointerType");
415+
return;
416+
}
357417

358-
// No exception decl indicates '...', a catch-all.
359-
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
360-
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
361-
s.getHandler(i));
418+
CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
419+
getLoc(catchStmt->getSourceRange()), caughtType,
420+
catchStmt->getCaughtType());
421+
catchScope->setHandler(i, typeInfo, handler, catchStmt);
422+
} else {
423+
// No exception decl indicates '...', a catch-all.
424+
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler,
425+
s.getHandler(i));
426+
}
362427

363428
// Under async exceptions, catch(...) needs to catch HW exception too
364429
// Mark scope with SehTryBegin as a SEH __try scope
@@ -397,6 +462,9 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
397462
return;
398463
}
399464

465+
// Emit the structure of the EH dispatch for this catch.
466+
emitCatchDispatchBlock(*this, catchScope, tryOp);
467+
400468
// Copy the handler blocks off before we pop the EH stack. Emitting
401469
// the handlers might scribble on this memory.
402470
SmallVector<EHCatchScope::Handler> handlers(catchScope.begin(),
@@ -442,9 +510,6 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
442510
emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
443511
assert(emitResult.succeeded() && "failed to emit catch handler block");
444512

445-
assert(!cir::MissingFeatures::catchParamOp());
446-
cir::YieldOp::create(builder, tryOp->getLoc());
447-
448513
// [except.handle]p11:
449514
// The currently handled exception is rethrown if control
450515
// reaches the end of a handler of the function-try-block of a
@@ -501,7 +566,8 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
501566
if (!handlerTypesAttr || handlerTypesAttr.empty()) {
502567
// Accumulate all the handlers in scope.
503568
bool hasCatchAll = false;
504-
llvm::SmallVector<mlir::Attribute, 4> handlerAttrs;
569+
llvm::SmallPtrSet<mlir::Attribute, 4> catchTypes;
570+
llvm::SmallVector<mlir::Attribute> handlerAttrs;
505571
for (EHScopeStack::iterator i = ehStack.begin(), e = ehStack.end(); i != e;
506572
++i) {
507573
switch (i->getKind()) {
@@ -534,20 +600,25 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
534600
break;
535601
}
536602

537-
cgm.errorNYI("emitLandingPad: non catch-all");
538-
return;
603+
// Check whether we already have a handler for this type.
604+
// If not, keep track to later add to catch op.
605+
if (catchTypes.insert(handler.type.rtti).second)
606+
handlerAttrs.push_back(handler.type.rtti);
539607
}
540608

541609
if (hasCatchAll)
542610
break;
543611
}
544612

545-
if (hasCatchAll) {
613+
if (hasCatchAll)
546614
handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
547-
} else {
548-
cgm.errorNYI("emitLandingPad: non catch-all");
549-
return;
550-
}
615+
616+
assert(!cir::MissingFeatures::ehScopeFilter());
617+
618+
// If there's no catch_all, attach the unwind region. This needs to be the
619+
// last region in the TryOp catch list.
620+
if (!hasCatchAll)
621+
handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
551622

552623
// Add final array of clauses into TryOp.
553624
tryOp.setHandlerTypesAttr(
@@ -571,6 +642,13 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
571642
return;
572643
}
573644

645+
// The dispatch block for the end of the scope chain is a block that
646+
// just resumes unwinding.
647+
if (scope == ehStack.stable_end()) {
648+
populateUnwindResumeBlock(/*isCleanup=*/true, tryOp);
649+
return;
650+
}
651+
574652
// Otherwise, we should look at the actual scope.
575653
EHScope &ehScope = *ehStack.find(scope);
576654
bool mayThrow = ehScope.mayThrow();
@@ -588,16 +666,19 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
588666
if (!mayThrow) {
589667
switch (ehScope.getKind()) {
590668
case EHScope::Catch: {
669+
mayThrow = true;
670+
591671
// LLVM does some optimization with branches here, CIR just keep track of
592672
// the corresponding calls.
593673
EHCatchScope &catchScope = cast<EHCatchScope>(ehScope);
594674
if (catchScope.getNumHandlers() == 1 &&
595675
catchScope.getHandler(0).isCatchAll()) {
596-
mayThrow = true;
597676
break;
598677
}
599-
cgm.errorNYI("getEHDispatchBlock: mayThrow non-catch all");
600-
return;
678+
679+
// TODO(cir): In the incubator we create a new basic block with YieldOp
680+
// inside the attached cleanup region, but this part will be redesigned
681+
break;
601682
}
602683
case EHScope::Cleanup: {
603684
cgm.errorNYI("getEHDispatchBlock: mayThrow & cleanup");

clang/lib/CIR/CodeGen/CIRGenFunction.h

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

977+
void populateUnwindResumeBlock(bool isCleanup, cir::TryOp tryOp);
977978
void populateEHCatchRegions(EHScopeStack::stable_iterator scope,
978979
cir::TryOp tryOp);
979980

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 82 additions & 2 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;
@@ -2310,7 +2317,6 @@ struct CallEndCatch final : EHScopeStack::Cleanup {
23102317

23112318
static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Type paramTy,
23122319
bool endMightThrow) {
2313-
23142320
auto catchParam = cir::CatchParamOp::create(
23152321
cgf.getBuilder(), cgf.getBuilder().getUnknownLoc(), paramTy);
23162322

@@ -2321,6 +2327,56 @@ static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Type paramTy,
23212327
return catchParam.getParam();
23222328
}
23232329

2330+
/// A "special initializer" callback for initializing a catch
2331+
/// parameter during catch initialization.
2332+
static void initCatchParam(CIRGenFunction &cgf, const VarDecl &catchParam,
2333+
Address paramAddr, SourceLocation loc) {
2334+
CanQualType catchType =
2335+
cgf.cgm.getASTContext().getCanonicalType(catchParam.getType());
2336+
// If we're catching by reference, we can just cast the object
2337+
// pointer to the appropriate pointer.
2338+
if (isa<ReferenceType>(catchType)) {
2339+
cgf.cgm.errorNYI(loc, "initCatchParam: ReferenceType");
2340+
return;
2341+
}
2342+
2343+
// Scalars and complexes.
2344+
cir::TypeEvaluationKind tek = cgf.getEvaluationKind(catchType);
2345+
if (tek != cir::TEK_Aggregate) {
2346+
// Notes for LLVM lowering:
2347+
// If the catch type is a pointer type, __cxa_begin_catch returns
2348+
// the pointer by value.
2349+
if (catchType->hasPointerRepresentation()) {
2350+
cgf.cgm.errorNYI(loc, "initCatchParam: hasPointerRepresentation");
2351+
return;
2352+
}
2353+
2354+
mlir::Type cirCatchTy = cgf.convertTypeForMem(catchType);
2355+
mlir::Value catchParam =
2356+
callBeginCatch(cgf, cgf.getBuilder().getPointerTo(cirCatchTy), false);
2357+
LValue srcLV = cgf.makeNaturalAlignAddrLValue(catchParam, catchType);
2358+
LValue destLV = cgf.makeAddrLValue(paramAddr, catchType);
2359+
switch (tek) {
2360+
case cir::TEK_Complex: {
2361+
cgf.cgm.errorNYI(loc, "initCatchParam: cir::TEK_Complex");
2362+
return;
2363+
}
2364+
case cir::TEK_Scalar: {
2365+
auto exnLoad = cgf.emitLoadOfScalar(srcLV, loc);
2366+
cgf.emitStoreOfScalar(exnLoad, destLV, /*isInit=*/true);
2367+
return;
2368+
}
2369+
case cir::TEK_Aggregate:
2370+
llvm_unreachable("evaluation kind filtered out!");
2371+
}
2372+
2373+
// Otherwise, it returns a pointer into the exception object.
2374+
llvm_unreachable("bad evaluation kind");
2375+
}
2376+
2377+
cgf.cgm.errorNYI(loc, "initCatchParam: cir::TEK_Aggregate");
2378+
}
2379+
23242380
/// Begins a catch statement by initializing the catch variable and
23252381
/// calling __cxa_begin_catch.
23262382
void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
@@ -2355,5 +2411,29 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
23552411
return;
23562412
}
23572413

2358-
cgf.cgm.errorNYI("emitBeginCatch: catch with exception decl");
2414+
auto getCatchParamAllocaIP = [&]() {
2415+
cir::CIRBaseBuilderTy::InsertPoint currIns =
2416+
cgf.getBuilder().saveInsertionPoint();
2417+
mlir::Operation *currParent = currIns.getBlock()->getParentOp();
2418+
2419+
mlir::Block *insertBlock = nullptr;
2420+
if (auto scopeOp = currParent->getParentOfType<cir::ScopeOp>()) {
2421+
insertBlock = &scopeOp.getScopeRegion().getBlocks().back();
2422+
} else if (auto fnOp = currParent->getParentOfType<cir::FuncOp>()) {
2423+
insertBlock = &fnOp.getRegion().getBlocks().back();
2424+
} else {
2425+
llvm_unreachable("unknown outermost scope-like parent");
2426+
}
2427+
return cgf.getBuilder().getBestAllocaInsertPoint(insertBlock);
2428+
};
2429+
2430+
// Emit the local. Make sure the alloca's superseed the current scope, since
2431+
// these are going to be consumed by `cir.catch`, which is not within the
2432+
// current scope.
2433+
2434+
CIRGenFunction::AutoVarEmission var =
2435+
cgf.emitAutoVarAlloca(*catchParam, getCatchParamAllocaIP());
2436+
initCatchParam(cgf, *catchParam, var.getObjectAddress(cgf),
2437+
catchStmt->getBeginLoc());
2438+
cgf.emitAutoVarCleanups(var);
23592439
}

0 commit comments

Comments
 (0)