Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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"
];

Expand Down Expand Up @@ -2956,6 +2956,9 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {

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

let skipDefaultBuilders = 1;

let builders = [
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
Expand All @@ -2965,6 +2968,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();
}]>
];
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
149 changes: 133 additions & 16 deletions clang/lib/CIR/CodeGen/CIRGenException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -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();
Expand All @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,16 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
return mlir::failure();

// If exception is present and there are cleanups, this should be latest thing
// present (after all attributes, etc).
if (!hasDestinationBlocks) {
mlir::Region *cleanupRegion = result.addRegion();
if (parser.parseOptionalKeyword("cleanup").succeeded()) {
if (parser.parseRegion(*cleanupRegion))
return failure();
}
}

return mlir::success();
}

Expand Down Expand Up @@ -893,6 +903,14 @@ static void printCallCommon(mlir::Operation *op,
printer << " : ";
printer.printFunctionalType(op->getOperands().getTypes(),
op->getResultTypes());

// If exception is present and there are cleanups, this should be latest thing
// present (after all attributes, etc).
auto call = dyn_cast<cir::CallOp>(op);
if (call && !call.getCleanup().empty()) {
printer << " cleanup ";
printer.printRegion(call.getCleanup());
}
}

mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
Expand Down
56 changes: 55 additions & 1 deletion clang/test/CIR/CodeGen/try-catch-tmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

int division();

void calling_division_inside_try_block() {
void call_function_inside_try_catch_all() {
try {
division();
} catch (...) {
Expand Down Expand Up @@ -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]]
Loading
Loading