Skip to content

Commit 8d97328

Browse files
committed
[CIR] Upstream Exception CXXTryStmt with only catch all
1 parent 90e0933 commit 8d97328

File tree

10 files changed

+456
-4
lines changed

10 files changed

+456
-4
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct MissingFeatures {
119119
static bool opCallLandingPad() { return false; }
120120
static bool opCallContinueBlock() { return false; }
121121
static bool opCallChain() { return false; }
122+
static bool opCallExceptionAttr() { return false; }
122123

123124
// CXXNewExpr
124125
static bool exprNewNullCheck() { return false; }

clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ CIRGenCXXABI::AddedStructorArgCounts CIRGenCXXABI::addImplicitConstructorArgs(
3737
addedArgs.suffix.size());
3838
}
3939

40+
CatchTypeInfo CIRGenCXXABI::getCatchAllTypeInfo() {
41+
return CatchTypeInfo{nullptr, 0};
42+
}
43+
4044
void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf,
4145
FunctionArgList &params) {
4246
const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl());

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H
1616

1717
#include "CIRGenCall.h"
18+
#include "CIRGenCleanup.h"
1819
#include "CIRGenFunction.h"
1920
#include "CIRGenModule.h"
2021

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

159+
virtual CatchTypeInfo getCatchAllTypeInfo();
160+
158161
/// Get the implicit (second) parameter that comes after the "this" pointer,
159162
/// or nullptr if there is isn't one.
160163
virtual mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf,

clang/lib/CIR/CodeGen/CIRGenCleanup.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ void EHScopeStack::popCleanup() {
108108
assert(!cir::MissingFeatures::ehCleanupBranchFixups());
109109
}
110110

111+
EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) {
112+
char *buffer = allocate(EHCatchScope::getSizeForNumHandlers(numHandlers));
113+
assert(!cir::MissingFeatures::innermostEHScope());
114+
EHCatchScope *scope = new (buffer) EHCatchScope(numHandlers);
115+
return scope;
116+
}
117+
111118
static void emitCleanup(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup) {
112119
// Ask the cleanup to emit itself.
113120
assert(cgf.haveInsertPoint() && "expected insertion point");

clang/lib/CIR/CodeGen/CIRGenCleanup.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020

2121
namespace clang::CIRGen {
2222

23+
/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the
24+
/// type of a catch handler, so we use this wrapper.
25+
struct CatchTypeInfo {
26+
mlir::TypedAttr rtti;
27+
unsigned flags;
28+
};
29+
2330
/// A protected scope for zero-cost EH handling.
2431
class EHScope {
2532
class CommonBitFields {
@@ -29,6 +36,12 @@ class EHScope {
2936
enum { NumCommonBits = 3 };
3037

3138
protected:
39+
class CatchBitFields {
40+
friend class EHCatchScope;
41+
unsigned : NumCommonBits;
42+
unsigned numHandlers : 32 - NumCommonBits;
43+
};
44+
3245
class CleanupBitFields {
3346
friend class EHCleanupScope;
3447
unsigned : NumCommonBits;
@@ -58,6 +71,7 @@ class EHScope {
5871

5972
union {
6073
CommonBitFields commonBits;
74+
CatchBitFields catchBits;
6175
CleanupBitFields cleanupBits;
6276
};
6377

@@ -67,6 +81,72 @@ class EHScope {
6781
EHScope(Kind kind) { commonBits.kind = kind; }
6882

6983
Kind getKind() const { return static_cast<Kind>(commonBits.kind); }
84+
85+
bool hasEHBranches() const {
86+
// Traditional LLVM codegen also checks for `!block->use_empty()`, but
87+
// in CIRGen the block content is not important, just used as a way to
88+
// signal `hasEHBranches`.
89+
assert(!cir::MissingFeatures::ehstackBranches());
90+
return false;
91+
}
92+
};
93+
94+
/// A scope which attempts to handle some, possibly all, types of
95+
/// exceptions.
96+
///
97+
/// Objective C \@finally blocks are represented using a cleanup scope
98+
/// after the catch scope.
99+
100+
class EHCatchScope : public EHScope {
101+
// In effect, we have a flexible array member
102+
// Handler Handlers[0];
103+
// But that's only standard in C99, not C++, so we have to do
104+
// annoying pointer arithmetic instead.
105+
106+
public:
107+
struct Handler {
108+
/// A type info value, or null (C++ null, not an LLVM null pointer)
109+
/// for a catch-all.
110+
CatchTypeInfo type;
111+
112+
/// The catch handler for this type.
113+
mlir::Block *block;
114+
};
115+
116+
private:
117+
friend class EHScopeStack;
118+
119+
Handler *getHandlers() { return reinterpret_cast<Handler *>(this + 1); }
120+
121+
public:
122+
static size_t getSizeForNumHandlers(unsigned n) {
123+
return sizeof(EHCatchScope) + n * sizeof(Handler);
124+
}
125+
126+
EHCatchScope(unsigned numHandlers) : EHScope(Catch) {
127+
catchBits.numHandlers = numHandlers;
128+
assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?");
129+
}
130+
131+
unsigned getNumHandlers() const { return catchBits.numHandlers; }
132+
133+
void setHandler(unsigned i, CatchTypeInfo type, mlir::Block *block) {
134+
assert(i < getNumHandlers());
135+
getHandlers()[i].type = type;
136+
getHandlers()[i].block = block;
137+
}
138+
139+
// Clear all handler blocks.
140+
// FIXME: it's better to always call clearHandlerBlocks in DTOR and have a
141+
// 'takeHandler' or some such function which removes ownership from the
142+
// EHCatchScope object if the handlers should live longer than EHCatchScope.
143+
void clearHandlerBlocks() {
144+
// The blocks are owned by TryOp, nothing to delete.
145+
}
146+
147+
static bool classof(const EHScope *scope) {
148+
return scope->getKind() == Catch;
149+
}
70150
};
71151

72152
/// A cleanup scope which generates the cleanup blocks lazily.
@@ -147,5 +227,13 @@ EHScopeStack::find(stable_iterator savePoint) const {
147227
return iterator(endOfBuffer - savePoint.size);
148228
}
149229

230+
inline void EHScopeStack::popCatch() {
231+
assert(!empty() && "popping exception stack when not empty");
232+
233+
EHCatchScope &scope = llvm::cast<EHCatchScope>(*begin());
234+
assert(!cir::MissingFeatures::innermostEHScope());
235+
deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));
236+
}
237+
150238
} // namespace clang::CIRGen
151239
#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H

clang/lib/CIR/CodeGen/CIRGenException.cpp

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,151 @@ mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
6969
if (s.getTryBlock()->body_empty())
7070
return mlir::LogicalResult::success();
7171

72-
cgm.errorNYI("exitCXXTryStmt: CXXTryStmt with non-empty body");
73-
return mlir::LogicalResult::success();
72+
mlir::Location loc = getLoc(s.getSourceRange());
73+
// Create a scope to hold try local storage for catch params.
74+
75+
mlir::OpBuilder::InsertPoint scopeIP;
76+
cir::ScopeOp::create(
77+
builder, loc,
78+
/*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
79+
scopeIP = builder.saveInsertionPoint();
80+
});
81+
82+
mlir::OpBuilder::InsertionGuard guard(builder);
83+
builder.restoreInsertionPoint(scopeIP);
84+
mlir::LogicalResult result = emitCXXTryStmtUnderScope(s);
85+
cir::YieldOp::create(builder, loc);
86+
return result;
87+
}
88+
89+
mlir::LogicalResult
90+
CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) {
91+
const llvm::Triple &t = getTarget().getTriple();
92+
// If we encounter a try statement on in an OpenMP target region offloaded to
93+
// a GPU, we treat it as a basic block.
94+
const bool isTargetDevice =
95+
(cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
96+
if (isTargetDevice) {
97+
cgm.errorNYI(
98+
"emitCXXTryStmtUnderScope: OpenMP target region offloaded to GPU");
99+
return mlir::success();
100+
}
101+
102+
unsigned numHandlers = s.getNumHandlers();
103+
mlir::Location tryLoc = getLoc(s.getBeginLoc());
104+
mlir::OpBuilder::InsertPoint beginInsertTryBody;
105+
106+
bool hasCatchAll = false;
107+
for (unsigned i = 0; i != numHandlers; ++i) {
108+
hasCatchAll |= s.getHandler(i)->getExceptionDecl() == nullptr;
109+
if (hasCatchAll)
110+
break;
111+
}
112+
113+
// Create the scope to represent only the C/C++ `try {}` part. However,
114+
// don't populate right away. Reserve some space to store the exception
115+
// info but don't emit the bulk right away, for now only make sure the
116+
// scope returns the exception information.
117+
auto tryOp = cir::TryOp::create(
118+
builder, tryLoc,
119+
/*tryBuilder=*/
120+
[&](mlir::OpBuilder &b, mlir::Location loc) {
121+
beginInsertTryBody = builder.saveInsertionPoint();
122+
},
123+
/*handlersBuilder=*/
124+
[&](mlir::OpBuilder &b, mlir::Location loc,
125+
mlir::OperationState &result) {
126+
mlir::OpBuilder::InsertionGuard guard(b);
127+
128+
// We create an extra region for an unwind catch handler in case the
129+
// catch-all handler doesn't exists
130+
unsigned numRegionsToCreate =
131+
hasCatchAll ? numHandlers : numHandlers + 1;
132+
133+
for (unsigned i = 0; i != numRegionsToCreate; ++i)
134+
builder.createBlock(result.addRegion());
135+
});
136+
137+
// Finally emit the body for try/catch.
138+
{
139+
mlir::Location loc = tryOp.getLoc();
140+
mlir::OpBuilder::InsertionGuard guard(builder);
141+
builder.restoreInsertionPoint(beginInsertTryBody);
142+
CIRGenFunction::LexicalScope tryScope{*this, loc,
143+
builder.getInsertionBlock()};
144+
145+
tryScope.setAsTry(tryOp);
146+
147+
// Attach the basic blocks for the catch regions.
148+
enterCXXTryStmt(s, tryOp);
149+
150+
// Emit the body for the `try {}` part.
151+
{
152+
mlir::OpBuilder::InsertionGuard guard(builder);
153+
CIRGenFunction::LexicalScope tryBodyScope{*this, loc,
154+
builder.getInsertionBlock()};
155+
if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
156+
return mlir::failure();
157+
}
158+
159+
// Emit catch clauses.
160+
exitCXXTryStmt(s);
161+
}
162+
163+
return mlir::success();
164+
}
165+
166+
void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
167+
bool isFnTryBlock) {
168+
unsigned numHandlers = s.getNumHandlers();
169+
EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
170+
for (unsigned i = 0; i != numHandlers; ++i) {
171+
const CXXCatchStmt *catchStmt = s.getHandler(i);
172+
if (catchStmt->getExceptionDecl()) {
173+
cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
174+
return;
175+
}
176+
177+
// No exception decl indicates '...', a catch-all.
178+
mlir::Block *handler = &tryOp.getHandlerRegions()[i].getBlocks().front();
179+
catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler);
180+
181+
// Under async exceptions, catch(...) need to catch HW exception too
182+
// Mark scope with SehTryBegin as a SEH __try scope
183+
if (getLangOpts().EHAsynch) {
184+
cgm.errorNYI("enterCXXTryStmt: EHAsynch");
185+
return;
186+
}
187+
}
188+
}
189+
190+
void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
191+
unsigned numHandlers = s.getNumHandlers();
192+
EHCatchScope &catchScope = cast<EHCatchScope>(*ehStack.begin());
193+
assert(catchScope.getNumHandlers() == numHandlers);
194+
cir::TryOp tryOp = curLexScope->getTry();
195+
196+
// If the catch was not required, bail out now.
197+
if (!catchScope.hasEHBranches()) {
198+
catchScope.clearHandlerBlocks();
199+
ehStack.popCatch();
200+
201+
// Drop all basic block from all catch regions.
202+
SmallVector<mlir::Block *> eraseBlocks;
203+
for (mlir::Region &handlerRegion : tryOp.getHandlerRegions()) {
204+
if (handlerRegion.empty())
205+
continue;
206+
207+
for (mlir::Block &b : handlerRegion.getBlocks())
208+
eraseBlocks.push_back(&b);
209+
}
210+
211+
for (mlir::Block *b : eraseBlocks)
212+
b->erase();
213+
214+
tryOp.setHandlerTypesAttr({});
215+
return;
216+
}
217+
218+
cgm.errorNYI("exitCXXTryStmt: Required catch");
74219
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,9 @@ class CIRGenFunction : public CIRGenTypeCache {
942942

943943
LexicalScope *parentScope = nullptr;
944944

945+
// Holds actual value for ScopeKind::Try
946+
cir::TryOp tryOp = nullptr;
947+
945948
// Only Regular is used at the moment. Support for other kinds will be
946949
// added as the relevant statements/expressions are upstreamed.
947950
enum Kind {
@@ -1001,6 +1004,10 @@ class CIRGenFunction : public CIRGenTypeCache {
10011004
void setAsGlobalInit() { scopeKind = Kind::GlobalInit; }
10021005
void setAsSwitch() { scopeKind = Kind::Switch; }
10031006
void setAsTernary() { scopeKind = Kind::Ternary; }
1007+
void setAsTry(cir::TryOp op) {
1008+
scopeKind = Kind::Try;
1009+
tryOp = op;
1010+
}
10041011

10051012
// Lazy create cleanup block or return what's available.
10061013
mlir::Block *getOrCreateCleanupBlock(mlir::OpBuilder &builder) {
@@ -1010,6 +1017,11 @@ class CIRGenFunction : public CIRGenTypeCache {
10101017
return cleanupBlock;
10111018
}
10121019

1020+
cir::TryOp getTry() {
1021+
assert(isTry());
1022+
return tryOp;
1023+
}
1024+
10131025
mlir::Block *getCleanupBlock(mlir::OpBuilder &builder) {
10141026
return cleanupBlock;
10151027
}
@@ -1331,6 +1343,13 @@ class CIRGenFunction : public CIRGenTypeCache {
13311343

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

1346+
mlir::LogicalResult emitCXXTryStmtUnderScope(const clang::CXXTryStmt &s);
1347+
1348+
void enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
1349+
bool isFnTryBlock = false);
1350+
1351+
void exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock = false);
1352+
13341353
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
13351354
clang::CXXCtorType ctorType, FunctionArgList &args);
13361355

clang/lib/CIR/CodeGen/EHScopeStack.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ class EHScopeStack {
155155
/// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
156156
void popCleanup();
157157

158+
/// Push a set of catch handlers on the stack. The catch is
159+
/// uninitialized and will need to have the given number of handlers
160+
/// set on it.
161+
class EHCatchScope *pushCatch(unsigned numHandlers);
162+
163+
/// Pops a catch scope off the stack. This is private to CIRGenException.cpp.
164+
void popCatch();
165+
158166
/// Determines whether the exception-scopes stack is empty.
159167
bool empty() const { return startOfData == endOfBuffer; }
160168

0 commit comments

Comments
 (0)