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 @@ -835,7 +835,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 @@ -3015,6 +3015,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 @@ -3024,6 +3027,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
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,6 @@ struct MissingFeatures {
static bool tryOp() { return false; }
static bool vecTernaryOp() { return false; }
static bool zextOp() { return false; }
static bool catchParamOp() { return false; }

// Future CIR attributes
static bool optInfoAttr() { return false; }
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 @@ -161,6 +161,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
152 changes: 133 additions & 19 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 @@ -430,9 +524,6 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
assert(emitResult.succeeded() && "failed to emit catch handler block");

assert(!cir::MissingFeatures::catchParamOp());
cir::YieldOp::create(builder, tryOp->getLoc());

// [except.handle]p11:
// The currently handled exception is rethrown if control
// reaches the end of a handler of the function-try-block of a
Expand Down Expand Up @@ -487,9 +578,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 @@ -522,8 +615,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 @@ -532,9 +627,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 @@ -559,6 +657,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 @@ -576,16 +681,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 @@ -974,6 +974,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
Loading