clang 22.0.0git
CIRGenException.cpp
Go to the documentation of this file.
1//===--- CIRGenException.cpp - Emit CIR Code for C++ exceptions -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This contains code dealing with C++ exception related code generation.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenCXXABI.h"
14#include "CIRGenFunction.h"
15
17
18using namespace clang;
19using namespace clang::CIRGen;
20
21const EHPersonality EHPersonality::GNU_C = {"__gcc_personality_v0", nullptr};
22const EHPersonality EHPersonality::GNU_C_SJLJ = {"__gcc_personality_sj0",
23 nullptr};
24const EHPersonality EHPersonality::GNU_C_SEH = {"__gcc_personality_seh0",
25 nullptr};
26const EHPersonality EHPersonality::NeXT_ObjC = {"__objc_personality_v0",
27 nullptr};
28const EHPersonality EHPersonality::GNU_CPlusPlus = {"__gxx_personality_v0",
29 nullptr};
31 "__gxx_personality_sj0", nullptr};
33 "__gxx_personality_seh0", nullptr};
34const EHPersonality EHPersonality::GNU_ObjC = {"__gnu_objc_personality_v0",
35 "objc_exception_throw"};
37 "__gnu_objc_personality_sj0", "objc_exception_throw"};
39 "__gnu_objc_personality_seh0", "objc_exception_throw"};
41 "__gnustep_objcxx_personality_v0", nullptr};
43 "__gnustep_objc_personality_v0", nullptr};
44const EHPersonality EHPersonality::MSVC_except_handler = {"_except_handler3",
45 nullptr};
47 "__C_specific_handler", nullptr};
49 "__CxxFrameHandler3", nullptr};
51 "__gxx_wasm_personality_v0", nullptr};
52const EHPersonality EHPersonality::XL_CPlusPlus = {"__xlcxx_personality_v1",
53 nullptr};
54const EHPersonality EHPersonality::ZOS_CPlusPlus = {"__zos_cxx_personality_v2",
55 nullptr};
56
57static const EHPersonality &getCPersonality(const TargetInfo &target,
58 const CodeGenOptions &cgOpts) {
59 const llvm::Triple &triple = target.getTriple();
60 if (triple.isWindowsMSVCEnvironment())
62 if (cgOpts.hasSjLjExceptions())
64 if (cgOpts.hasDWARFExceptions())
66 if (cgOpts.hasSEHExceptions())
69}
70
71static const EHPersonality &getObjCPersonality(const TargetInfo &target,
72 const LangOptions &langOpts,
73 const CodeGenOptions &cgOpts) {
74 const llvm::Triple &triple = target.getTriple();
75 if (triple.isWindowsMSVCEnvironment())
77
78 switch (langOpts.ObjCRuntime.getKind()) {
80 return getCPersonality(target, cgOpts);
86 if (langOpts.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
88 [[fallthrough]];
91 if (cgOpts.hasSjLjExceptions())
93 if (cgOpts.hasSEHExceptions())
96 }
97 llvm_unreachable("bad runtime kind");
98}
99
100static const EHPersonality &getCXXPersonality(const TargetInfo &target,
101 const CodeGenOptions &cgOpts) {
102 const llvm::Triple &triple = target.getTriple();
103 if (triple.isWindowsMSVCEnvironment())
105 if (triple.isOSAIX())
107 if (cgOpts.hasSjLjExceptions())
109 if (cgOpts.hasDWARFExceptions())
111 if (cgOpts.hasSEHExceptions())
113 if (cgOpts.hasWasmExceptions())
116}
117
118/// Determines the personality function to use when both C++
119/// and Objective-C exceptions are being caught.
121 const LangOptions &langOpts,
122 const CodeGenOptions &cgOpts) {
123 if (target.getTriple().isWindowsMSVCEnvironment())
125
126 switch (langOpts.ObjCRuntime.getKind()) {
127 // In the fragile ABI, just use C++ exception handling and hope
128 // they're not doing crazy exception mixing.
130 return getCXXPersonality(target, cgOpts);
131
132 // The ObjC personality defers to the C++ personality for non-ObjC
133 // handlers. Unlike the C++ case, we use the same personality
134 // function on targets using (backend-driven) SJLJ EH.
136 case ObjCRuntime::iOS:
138 return getObjCPersonality(target, langOpts, cgOpts);
139
142
143 // The GCC runtime's personality function inherently doesn't support
144 // mixed EH. Use the ObjC personality just to avoid returning null.
145 case ObjCRuntime::GCC:
147 return getObjCPersonality(target, langOpts, cgOpts);
148 }
149 llvm_unreachable("bad runtime kind");
150}
151
152static const EHPersonality &getSEHPersonalityMSVC(const llvm::Triple &triple) {
153 return triple.getArch() == llvm::Triple::x86
156}
157
159 const FunctionDecl *fd) {
160 const llvm::Triple &triple = cgm.getTarget().getTriple();
161 const LangOptions &langOpts = cgm.getLangOpts();
162 const CodeGenOptions &cgOpts = cgm.getCodeGenOpts();
163 const TargetInfo &target = cgm.getTarget();
164
165 // Functions using SEH get an SEH personality.
166 if (fd && fd->usesSEHTry())
167 return getSEHPersonalityMSVC(triple);
168
169 if (langOpts.ObjC) {
170 return langOpts.CPlusPlus ? getObjCXXPersonality(target, langOpts, cgOpts)
171 : getObjCPersonality(target, langOpts, cgOpts);
172 }
173 return langOpts.CPlusPlus ? getCXXPersonality(target, cgOpts)
174 : getCPersonality(target, cgOpts);
175}
176
178 const auto *fg = cgf.curCodeDecl;
179 // For outlined finallys and filters, use the SEH personality in case they
180 // contain more SEH. This mostly only affects finallys. Filters could
181 // hypothetically use gnu statement expressions to sneak in nested SEH.
182 fg = fg ? fg : cgf.curSEHParent.getDecl();
183 return get(cgf.cgm, dyn_cast_or_null<FunctionDecl>(fg));
184}
185
187 const llvm::Triple &triple = getTarget().getTriple();
188 if (cgm.getLangOpts().OpenMPIsTargetDevice &&
189 (triple.isNVPTX() || triple.isAMDGCN())) {
190 cgm.errorNYI("emitCXXThrowExpr OpenMP with NVPTX or AMDGCN Triples");
191 return;
192 }
193
194 if (const Expr *subExpr = e->getSubExpr()) {
195 QualType throwType = subExpr->getType();
196 if (throwType->isObjCObjectPointerType()) {
197 cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
198 return;
199 }
200
201 cgm.getCXXABI().emitThrow(*this, e);
202 return;
203 }
204
205 cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
206}
207
209 // Make sure the exception object is cleaned up if there's an
210 // exception during initialization.
212
213 // __cxa_allocate_exception returns a void*; we need to cast this
214 // to the appropriate type for the object.
215 mlir::Type ty = convertTypeForMem(e->getType());
216 Address typedAddr = addr.withElementType(builder, ty);
217
218 // From LLVM's codegen:
219 // FIXME: this isn't quite right! If there's a final unelided call
220 // to a copy constructor, then according to [except.terminate]p1 we
221 // must call std::terminate() if that constructor throws, because
222 // technically that copy occurs after the exception expression is
223 // evaluated but before the exception is caught. But the best way
224 // to handle that is to teach EmitAggExpr to do the final copy
225 // differently if it can't be elided.
226 emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
227 /*isInitializer=*/true);
228
229 // Deactivate the cleanup block.
231}
232
233mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
234 if (s.getTryBlock()->body_empty())
235 return mlir::LogicalResult::success();
236
237 mlir::Location loc = getLoc(s.getSourceRange());
238 // Create a scope to hold try local storage for catch params.
239
240 mlir::OpBuilder::InsertPoint scopeIP;
241 cir::ScopeOp::create(
242 builder, loc,
243 /*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
244 scopeIP = builder.saveInsertionPoint();
245 });
246
247 mlir::OpBuilder::InsertionGuard guard(builder);
248 builder.restoreInsertionPoint(scopeIP);
249 mlir::LogicalResult result = emitCXXTryStmtUnderScope(s);
250 cir::YieldOp::create(builder, loc);
251 return result;
252}
253
254mlir::LogicalResult
256 const llvm::Triple &t = getTarget().getTriple();
257 // If we encounter a try statement on in an OpenMP target region offloaded to
258 // a GPU, we treat it as a basic block.
259 const bool isTargetDevice =
260 (cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
261 if (isTargetDevice) {
262 cgm.errorNYI(
263 "emitCXXTryStmtUnderScope: OpenMP target region offloaded to GPU");
264 return mlir::success();
265 }
266
267 unsigned numHandlers = s.getNumHandlers();
268 mlir::Location tryLoc = getLoc(s.getBeginLoc());
269 mlir::OpBuilder::InsertPoint beginInsertTryBody;
270
271 bool hasCatchAll = false;
272 for (unsigned i = 0; i != numHandlers; ++i) {
273 hasCatchAll |= s.getHandler(i)->getExceptionDecl() == nullptr;
274 if (hasCatchAll)
275 break;
276 }
277
278 // Create the scope to represent only the C/C++ `try {}` part. However,
279 // don't populate right away. Create regions for the catch handlers,
280 // but don't emit the handler bodies yet. For now, only make sure the
281 // scope returns the exception information.
282 auto tryOp = cir::TryOp::create(
283 builder, tryLoc,
284 /*tryBuilder=*/
285 [&](mlir::OpBuilder &b, mlir::Location loc) {
286 beginInsertTryBody = builder.saveInsertionPoint();
287 },
288 /*handlersBuilder=*/
289 [&](mlir::OpBuilder &b, mlir::Location loc,
290 mlir::OperationState &result) {
291 mlir::OpBuilder::InsertionGuard guard(b);
292
293 // We create an extra region for an unwind catch handler in case the
294 // catch-all handler doesn't exists
295 unsigned numRegionsToCreate =
296 hasCatchAll ? numHandlers : numHandlers + 1;
297
298 for (unsigned i = 0; i != numRegionsToCreate; ++i) {
299 mlir::Region *region = result.addRegion();
300 builder.createBlock(region);
301 }
302 });
303
304 // Finally emit the body for try/catch.
305 {
306 mlir::Location loc = tryOp.getLoc();
307 mlir::OpBuilder::InsertionGuard guard(builder);
308 builder.restoreInsertionPoint(beginInsertTryBody);
309 CIRGenFunction::LexicalScope tryScope{*this, loc,
310 builder.getInsertionBlock()};
311
312 tryScope.setAsTry(tryOp);
313
314 // Attach the basic blocks for the catch regions.
315 enterCXXTryStmt(s, tryOp);
316
317 // Emit the body for the `try {}` part.
318 {
319 mlir::OpBuilder::InsertionGuard guard(builder);
320 CIRGenFunction::LexicalScope tryBodyScope{*this, loc,
321 builder.getInsertionBlock()};
322 if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
323 return mlir::failure();
324 }
325
326 // Emit catch clauses.
328 }
329
330 return mlir::success();
331}
332
333void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
334 bool isFnTryBlock) {
335 unsigned numHandlers = s.getNumHandlers();
336 EHCatchScope *catchScope = ehStack.pushCatch(numHandlers);
337 for (unsigned i = 0; i != numHandlers; ++i) {
338 const CXXCatchStmt *catchStmt = s.getHandler(i);
339 if (catchStmt->getExceptionDecl()) {
340 cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl");
341 return;
342 }
343
344 // No exception decl indicates '...', a catch-all.
345 mlir::Region *handler = &tryOp.getHandlerRegions()[i];
346 catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler);
347
348 // Under async exceptions, catch(...) needs to catch HW exception too
349 // Mark scope with SehTryBegin as a SEH __try scope
350 if (getLangOpts().EHAsynch) {
351 cgm.errorNYI("enterCXXTryStmt: EHAsynch");
352 return;
353 }
354 }
355}
356
357void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
358 unsigned numHandlers = s.getNumHandlers();
359 EHCatchScope &catchScope = cast<EHCatchScope>(*ehStack.begin());
360 assert(catchScope.getNumHandlers() == numHandlers);
361 cir::TryOp tryOp = curLexScope->getTry();
362
363 // If the catch was not required, bail out now.
364 if (!catchScope.mayThrow()) {
365 catchScope.clearHandlerBlocks();
366 ehStack.popCatch();
367
368 // Drop all basic block from all catch regions.
369 SmallVector<mlir::Block *> eraseBlocks;
370 for (mlir::Region &handlerRegion : tryOp.getHandlerRegions()) {
371 if (handlerRegion.empty())
372 continue;
373
374 for (mlir::Block &b : handlerRegion.getBlocks())
375 eraseBlocks.push_back(&b);
376 }
377
378 for (mlir::Block *b : eraseBlocks)
379 b->erase();
380
381 tryOp.setHandlerTypesAttr({});
382 return;
383 }
384
385 cgm.errorNYI("exitCXXTryStmt: Required catch");
386}
static const EHPersonality & getCXXPersonality(const TargetInfo &target, const CodeGenOptions &cgOpts)
static const EHPersonality & getCPersonality(const TargetInfo &target, const CodeGenOptions &cgOpts)
static const EHPersonality & getObjCPersonality(const TargetInfo &target, const LangOptions &langOpts, const CodeGenOptions &cgOpts)
static const EHPersonality & getObjCXXPersonality(const TargetInfo &target, const LangOptions &langOpts, const CodeGenOptions &cgOpts)
Determines the personality function to use when both C++ and Objective-C exceptions are being caught.
static const EHPersonality & getSEHPersonalityMSVC(const llvm::Triple &triple)
__device__ __2f16 b
__device__ __2f16 float __ockl_bool s
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const
Return address with different element type, a bitcast pointer, and the same alignment.
const clang::LangOptions & getLangOpts() const
void enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp, bool isFnTryBlock=false)
void exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock=false)
const TargetInfo & getTarget() const
void emitAnyExprToExn(const Expr *e, Address addr)
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, bool isInitializer)
Emits the code necessary to evaluate an arbitrary expression into the given memory location.
mlir::LogicalResult emitCXXTryStmtUnderScope(const clang::CXXTryStmt &s)
EHScopeStack ehStack
Tracks function scope overall cleanup handling.
mlir::Type convertTypeForMem(QualType t)
const clang::Decl * curCodeDecl
This is the inner-most code context, which includes blocks.
mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s)
void emitCXXThrowExpr(const CXXThrowExpr *e)
mlir::LogicalResult emitStmt(const clang::Stmt *s, bool useCurrentScope, llvm::ArrayRef< const Attr * > attrs={})
This class organizes the cross-function state that is used while generating CIR code.
const clang::TargetInfo & getTarget() const
const clang::CodeGenOptions & getCodeGenOpts() const
const clang::LangOptions & getLangOpts() const
A scope which attempts to handle some, possibly all, types of exceptions.
void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region)
unsigned getNumHandlers() const
CXXCatchStmt - This represents a C++ catch block.
Definition StmtCXX.h:28
VarDecl * getExceptionDecl() const
Definition StmtCXX.h:49
A C++ throw-expression (C++ [except.throw]).
Definition ExprCXX.h:1209
const Expr * getSubExpr() const
Definition ExprCXX.h:1229
CXXTryStmt - A C++ try block, including all handlers.
Definition StmtCXX.h:69
CodeGenOptions - Track various options which control how the code is optimized and passed to the back...
bool hasDWARFExceptions() const
bool hasWasmExceptions() const
bool hasSjLjExceptions() const
bool hasSEHExceptions() const
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
Represents a function declaration or definition.
Definition Decl.h:2000
bool usesSEHTry() const
Indicates the function uses __try.
Definition Decl.h:2518
const Decl * getDecl() const
Definition GlobalDecl.h:106
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
clang::ObjCRuntime ObjCRuntime
Kind getKind() const
Definition ObjCRuntime.h:77
const VersionTuple & getVersion() const
Definition ObjCRuntime.h:78
@ MacOSX
'macosx' is the Apple-provided NeXT-derived runtime on Mac OS X platforms that use the non-fragile AB...
Definition ObjCRuntime.h:35
@ FragileMacOSX
'macosx-fragile' is the Apple-provided NeXT-derived runtime on Mac OS X platforms that use the fragil...
Definition ObjCRuntime.h:40
@ GNUstep
'gnustep' is the modern non-fragile GNUstep runtime.
Definition ObjCRuntime.h:56
@ ObjFW
'objfw' is the Objective-C runtime included in ObjFW
Definition ObjCRuntime.h:59
@ iOS
'ios' is the Apple-provided NeXT-derived runtime on iOS or the iOS simulator; it is always non-fragil...
Definition ObjCRuntime.h:45
@ GCC
'gcc' is the Objective-C runtime shipped with GCC, implementing a fragile Objective-C ABI
Definition ObjCRuntime.h:53
@ WatchOS
'watchos' is a variant of iOS for Apple's watchOS.
Definition ObjCRuntime.h:49
A (possibly-)qualified type.
Definition TypeBase.h:937
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8318
Exposes information about the current target.
Definition TargetInfo.h:226
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isObjCObjectPointerType() const
Definition TypeBase.h:8684
The JSON file list parser is used to communicate input to InstallAPI.
U cast(CodeGen::Address addr)
Definition Address.h:327
static bool ehCleanupScope()
Represents a scope, including function bodies, compound statements, and the substatements of if/while...
The exceptions personality for a function.
static const EHPersonality XL_CPlusPlus
static const EHPersonality GNU_ObjC_SJLJ
static const EHPersonality ZOS_CPlusPlus
static const EHPersonality GNUstep_ObjC
static const EHPersonality MSVC_CxxFrameHandler3
static const EHPersonality MSVC_C_specific_handler
static const EHPersonality GNU_CPlusPlus_SEH
static const EHPersonality GNU_ObjC
static const EHPersonality GNU_CPlusPlus_SJLJ
static const EHPersonality GNU_C_SJLJ
static const EHPersonality GNU_C
static const EHPersonality NeXT_ObjC
static const EHPersonality & get(CIRGenModule &cgm, const clang::FunctionDecl *fd)
static const EHPersonality GNU_CPlusPlus
static const EHPersonality GNU_ObjCXX
static const EHPersonality GNU_C_SEH
static const EHPersonality MSVC_except_handler
static const EHPersonality GNU_ObjC_SEH
static const EHPersonality GNU_Wasm_CPlusPlus