Skip to content
Merged
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
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ struct MissingFeatures {
static bool opCallLandingPad() { return false; }
static bool opCallContinueBlock() { return false; }
static bool opCallChain() { return false; }
static bool opCallExceptionAttr() { return false; }

// CXXNewExpr
static bool exprNewNullCheck() { return false; }
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ CIRGenCXXABI::AddedStructorArgCounts CIRGenCXXABI::addImplicitConstructorArgs(
addedArgs.suffix.size());
}

CatchTypeInfo CIRGenCXXABI::getCatchAllTypeInfo() {
return CatchTypeInfo{{}, 0};
}

void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf,
FunctionArgList &params) {
const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl());
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 @@ -15,6 +15,7 @@
#define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H

#include "CIRGenCall.h"
#include "CIRGenCleanup.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"

Expand Down Expand Up @@ -155,6 +156,8 @@ class CIRGenCXXABI {
/// Loads the incoming C++ this pointer as it was passed by the caller.
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);

virtual CatchTypeInfo getCatchAllTypeInfo();

/// Get the implicit (second) parameter that comes after the "this" pointer,
/// or nullptr if there is isn't one.
virtual mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf,
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ void EHScopeStack::popCleanup() {
assert(!cir::MissingFeatures::ehCleanupBranchFixups());
}

EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) {
char *buffer = allocate(EHCatchScope::getSizeForNumHandlers(numHandlers));
assert(!cir::MissingFeatures::innermostEHScope());
EHCatchScope *scope = new (buffer) EHCatchScope(numHandlers);
return scope;
}

static void emitCleanup(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup) {
// Ask the cleanup to emit itself.
assert(cgf.haveInsertPoint() && "expected insertion point");
Expand Down
87 changes: 87 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCleanup.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@

namespace clang::CIRGen {

/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the
/// type of a catch handler, so we use this wrapper.
struct CatchTypeInfo {
mlir::TypedAttr rtti;
unsigned flags;
};

/// A protected scope for zero-cost EH handling.
class EHScope {
class CommonBitFields {
Expand All @@ -29,6 +36,12 @@ class EHScope {
enum { NumCommonBits = 3 };

protected:
class CatchBitFields {
friend class EHCatchScope;
unsigned : NumCommonBits;
unsigned numHandlers : 32 - NumCommonBits;
};

class CleanupBitFields {
friend class EHCleanupScope;
unsigned : NumCommonBits;
Expand Down Expand Up @@ -58,6 +71,7 @@ class EHScope {

union {
CommonBitFields commonBits;
CatchBitFields catchBits;
CleanupBitFields cleanupBits;
};

Expand All @@ -67,6 +81,71 @@ class EHScope {
EHScope(Kind kind) { commonBits.kind = kind; }

Kind getKind() const { return static_cast<Kind>(commonBits.kind); }

bool mayThrow() const {
// Traditional LLVM codegen also checks for `!block->use_empty()`, but
// in CIRGen the block content is not important, just used as a way to
// signal `hasEHBranches`.
assert(!cir::MissingFeatures::ehstackBranches());
return false;
}
};

/// A scope which attempts to handle some, possibly all, types of
/// exceptions.
///
/// Objective C \@finally blocks are represented using a cleanup scope
/// after the catch scope.

class EHCatchScope : public EHScope {
// In effect, we have a flexible array member
// Handler Handlers[0];
// But that's only standard in C99, not C++, so we have to do
// annoying pointer arithmetic instead.

public:
struct Handler {
/// A type info value, or null MLIR attribute for a catch-all
CatchTypeInfo type;

/// The catch handler for this type.
mlir::Region *region;
};

private:
friend class EHScopeStack;

Handler *getHandlers() { return reinterpret_cast<Handler *>(this + 1); }

public:
static size_t getSizeForNumHandlers(unsigned n) {
return sizeof(EHCatchScope) + n * sizeof(Handler);
}

EHCatchScope(unsigned numHandlers) : EHScope(Catch) {
catchBits.numHandlers = numHandlers;
assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?");
}

unsigned getNumHandlers() const { return catchBits.numHandlers; }

void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region) {
assert(i < getNumHandlers());
getHandlers()[i].type = type;
getHandlers()[i].region = region;
}

// Clear all handler blocks.
// FIXME: it's better to always call clearHandlerBlocks in DTOR and have a
// 'takeHandler' or some such function which removes ownership from the
// EHCatchScope object if the handlers should live longer than EHCatchScope.
void clearHandlerBlocks() {
// The blocks are owned by TryOp, nothing to delete.
}

static bool classof(const EHScope *scope) {
return scope->getKind() == Catch;
}
};

/// A cleanup scope which generates the cleanup blocks lazily.
Expand Down Expand Up @@ -147,5 +226,13 @@ EHScopeStack::find(stable_iterator savePoint) const {
return iterator(endOfBuffer - savePoint.size);
}

inline void EHScopeStack::popCatch() {
assert(!empty() && "popping exception stack when not empty");

EHCatchScope &scope = llvm::cast<EHCatchScope>(*begin());
assert(!cir::MissingFeatures::innermostEHScope());
deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));
}

} // namespace clang::CIRGen
#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H
151 changes: 149 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,153 @@ mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
if (s.getTryBlock()->body_empty())
return mlir::LogicalResult::success();

cgm.errorNYI("exitCXXTryStmt: CXXTryStmt with non-empty body");
return mlir::LogicalResult::success();
mlir::Location loc = getLoc(s.getSourceRange());
// Create a scope to hold try local storage for catch params.

mlir::OpBuilder::InsertPoint scopeIP;
cir::ScopeOp::create(
builder, loc,
/*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
scopeIP = builder.saveInsertionPoint();
});

mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(scopeIP);
mlir::LogicalResult result = emitCXXTryStmtUnderScope(s);
cir::YieldOp::create(builder, loc);
return result;
}

mlir::LogicalResult
CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
const llvm::Triple &t = getTarget().getTriple();
// If we encounter a try statement on in an OpenMP target region offloaded to
// a GPU, we treat it as a basic block.
const bool isTargetDevice =
(cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
if (isTargetDevice) {
cgm.errorNYI(
"emitCXXTryStmtUnderScope: OpenMP target region offloaded to GPU");
return mlir::success();
}

unsigned numHandlers = s.getNumHandlers();
mlir::Location tryLoc = getLoc(s.getBeginLoc());
mlir::OpBuilder::InsertPoint beginInsertTryBody;

bool hasCatchAll = false;
for (unsigned i = 0; i != numHandlers; ++i) {
hasCatchAll |= s.getHandler(i)->getExceptionDecl() == nullptr;
if (hasCatchAll)
break;
}

// Create the scope to represent only the C/C++ `try {}` part. However,
// don't populate right away. Create regions for the catch handlers,
// but don't emit the handler bodies yet. For now, only make sure the
// scope returns the exception information.
auto tryOp = cir::TryOp::create(
builder, tryLoc,
/*tryBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
beginInsertTryBody = builder.saveInsertionPoint();
},
/*handlersBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc,
mlir::OperationState &result) {
mlir::OpBuilder::InsertionGuard guard(b);

// We create an extra region for an unwind catch handler in case the
// catch-all handler doesn't exists
unsigned numRegionsToCreate =
hasCatchAll ? numHandlers : numHandlers + 1;

for (unsigned i = 0; i != numRegionsToCreate; ++i) {
mlir::Region *region = result.addRegion();
builder.createBlock(region);
}
});

// Finally emit the body for try/catch.
{
mlir::Location loc = tryOp.getLoc();
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(beginInsertTryBody);
CIRGenFunction::LexicalScope tryScope{*this, loc,
builder.getInsertionBlock()};

tryScope.setAsTry(tryOp);

// Attach the basic blocks for the catch regions.
enterCXXTryStmt(s, tryOp);

// Emit the body for the `try {}` part.
{
mlir::OpBuilder::InsertionGuard guard(builder);
CIRGenFunction::LexicalScope tryBodyScope{*this, loc,
builder.getInsertionBlock()};
if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
return mlir::failure();
}

// Emit catch clauses.
exitCXXTryStmt(s);
}

return mlir::success();
}

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);
if (catchStmt->getExceptionDecl()) {
cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
return;
}

// No exception decl indicates '...', a catch-all.
mlir::Region *handler = &tryOp.getHandlerRegions()[i];
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler);

// Under async exceptions, catch(...) needs to catch HW exception too
// Mark scope with SehTryBegin as a SEH __try scope
if (getLangOpts().EHAsynch) {
cgm.errorNYI("enterCXXTryStmt: EHAsynch");
return;
}
}
}

void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
unsigned numHandlers = s.getNumHandlers();
EHCatchScope &catchScope = cast<EHCatchScope>(*ehStack.begin());
assert(catchScope.getNumHandlers() == numHandlers);
cir::TryOp tryOp = curLexScope->getTry();

// If the catch was not required, bail out now.
if (!catchScope.mayThrow()) {
catchScope.clearHandlerBlocks();
ehStack.popCatch();

// Drop all basic block from all catch regions.
SmallVector<mlir::Block *> eraseBlocks;
for (mlir::Region &handlerRegion : tryOp.getHandlerRegions()) {
if (handlerRegion.empty())
continue;

for (mlir::Block &b : handlerRegion.getBlocks())
eraseBlocks.push_back(&b);
}

for (mlir::Block *b : eraseBlocks)
b->erase();

tryOp.setHandlerTypesAttr({});
return;
}

cgm.errorNYI("exitCXXTryStmt: Required catch");
}
19 changes: 19 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,9 @@ class CIRGenFunction : public CIRGenTypeCache {

LexicalScope *parentScope = nullptr;

// Holds the actual value for ScopeKind::Try
cir::TryOp tryOp = nullptr;

// Only Regular is used at the moment. Support for other kinds will be
// added as the relevant statements/expressions are upstreamed.
enum Kind {
Expand Down Expand Up @@ -1013,6 +1016,10 @@ class CIRGenFunction : public CIRGenTypeCache {
void setAsGlobalInit() { scopeKind = Kind::GlobalInit; }
void setAsSwitch() { scopeKind = Kind::Switch; }
void setAsTernary() { scopeKind = Kind::Ternary; }
void setAsTry(cir::TryOp op) {
scopeKind = Kind::Try;
tryOp = op;
}

// Lazy create cleanup block or return what's available.
mlir::Block *getOrCreateCleanupBlock(mlir::OpBuilder &builder) {
Expand All @@ -1022,6 +1029,11 @@ class CIRGenFunction : public CIRGenTypeCache {
return cleanupBlock;
}

cir::TryOp getTry() {
assert(isTry());
return tryOp;
}

mlir::Block *getCleanupBlock(mlir::OpBuilder &builder) {
return cleanupBlock;
}
Expand Down Expand Up @@ -1348,6 +1360,13 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s);

mlir::LogicalResult emitCXXTryStmtUnderScope(const clang::CXXTryStmt &s);

void enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
bool isFnTryBlock = false);

void exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock = false);

void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
clang::CXXCtorType ctorType, FunctionArgList &args);

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/EHScopeStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ class EHScopeStack {
/// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
void popCleanup();

/// Push a set of catch handlers on the stack. The catch is
/// uninitialized and will need to have the given number of handlers
/// set on it.
class EHCatchScope *pushCatch(unsigned numHandlers);

/// Pops a catch scope off the stack. This is private to CIRGenException.cpp.
void popCatch();

/// Determines whether the exception-scopes stack is empty.
bool empty() const { return startOfData == endOfBuffer; }

Expand Down
Loading
Loading