clang 23.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#include "mlir/IR/Block.h"
16#include "mlir/IR/Location.h"
17
19#include "llvm/Support/SaveAndRestore.h"
20
21using namespace clang;
22using namespace clang::CIRGen;
23
24const EHPersonality EHPersonality::GNU_C = {"__gcc_personality_v0", nullptr};
25const EHPersonality EHPersonality::GNU_C_SJLJ = {"__gcc_personality_sj0",
26 nullptr};
27const EHPersonality EHPersonality::GNU_C_SEH = {"__gcc_personality_seh0",
28 nullptr};
29const EHPersonality EHPersonality::NeXT_ObjC = {"__objc_personality_v0",
30 nullptr};
31const EHPersonality EHPersonality::GNU_CPlusPlus = {"__gxx_personality_v0",
32 nullptr};
34 "__gxx_personality_sj0", nullptr};
36 "__gxx_personality_seh0", nullptr};
37const EHPersonality EHPersonality::GNU_ObjC = {"__gnu_objc_personality_v0",
38 "objc_exception_throw"};
40 "__gnu_objc_personality_sj0", "objc_exception_throw"};
42 "__gnu_objc_personality_seh0", "objc_exception_throw"};
44 "__gnustep_objcxx_personality_v0", nullptr};
46 "__gnustep_objc_personality_v0", nullptr};
47const EHPersonality EHPersonality::MSVC_except_handler = {"_except_handler3",
48 nullptr};
50 "__C_specific_handler", nullptr};
52 "__CxxFrameHandler3", nullptr};
54 "__gxx_wasm_personality_v0", nullptr};
55const EHPersonality EHPersonality::XL_CPlusPlus = {"__xlcxx_personality_v1",
56 nullptr};
57const EHPersonality EHPersonality::ZOS_CPlusPlus = {"__zos_cxx_personality_v2",
58 nullptr};
59
60static const EHPersonality &getCPersonality(const TargetInfo &target,
61 const CodeGenOptions &cgOpts) {
62 const llvm::Triple &triple = target.getTriple();
63 if (triple.isWindowsMSVCEnvironment())
65 if (cgOpts.hasSjLjExceptions())
67 if (cgOpts.hasDWARFExceptions())
69 if (cgOpts.hasSEHExceptions())
72}
73
74static const EHPersonality &getObjCPersonality(const TargetInfo &target,
75 const LangOptions &langOpts,
76 const CodeGenOptions &cgOpts) {
77 const llvm::Triple &triple = target.getTriple();
78 if (triple.isWindowsMSVCEnvironment())
80
81 switch (langOpts.ObjCRuntime.getKind()) {
83 return getCPersonality(target, cgOpts);
89 if (langOpts.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
91 [[fallthrough]];
94 if (cgOpts.hasSjLjExceptions())
96 if (cgOpts.hasSEHExceptions())
99 }
100 llvm_unreachable("bad runtime kind");
101}
102
103static const EHPersonality &getCXXPersonality(const TargetInfo &target,
104 const CodeGenOptions &cgOpts) {
105 const llvm::Triple &triple = target.getTriple();
106 if (triple.isWindowsMSVCEnvironment())
108 if (triple.isOSAIX())
110 if (cgOpts.hasSjLjExceptions())
112 if (cgOpts.hasDWARFExceptions())
114 if (cgOpts.hasSEHExceptions())
116 if (cgOpts.hasWasmExceptions())
119}
120
121/// Determines the personality function to use when both C++
122/// and Objective-C exceptions are being caught.
124 const LangOptions &langOpts,
125 const CodeGenOptions &cgOpts) {
126 if (target.getTriple().isWindowsMSVCEnvironment())
128
129 switch (langOpts.ObjCRuntime.getKind()) {
130 // In the fragile ABI, just use C++ exception handling and hope
131 // they're not doing crazy exception mixing.
133 return getCXXPersonality(target, cgOpts);
134
135 // The ObjC personality defers to the C++ personality for non-ObjC
136 // handlers. Unlike the C++ case, we use the same personality
137 // function on targets using (backend-driven) SJLJ EH.
139 case ObjCRuntime::iOS:
141 return getObjCPersonality(target, langOpts, cgOpts);
142
145
146 // The GCC runtime's personality function inherently doesn't support
147 // mixed EH. Use the ObjC personality just to avoid returning null.
148 case ObjCRuntime::GCC:
150 return getObjCPersonality(target, langOpts, cgOpts);
151 }
152 llvm_unreachable("bad runtime kind");
153}
154
155static const EHPersonality &getSEHPersonalityMSVC(const llvm::Triple &triple) {
156 return triple.getArch() == llvm::Triple::x86
159}
160
162 const FunctionDecl *fd) {
163 const llvm::Triple &triple = cgm.getTarget().getTriple();
164 const LangOptions &langOpts = cgm.getLangOpts();
165 const CodeGenOptions &cgOpts = cgm.getCodeGenOpts();
166 const TargetInfo &target = cgm.getTarget();
167
168 // Functions using SEH get an SEH personality.
169 if (fd && fd->usesSEHTry())
170 return getSEHPersonalityMSVC(triple);
171
172 if (langOpts.ObjC) {
173 return langOpts.CPlusPlus ? getObjCXXPersonality(target, langOpts, cgOpts)
174 : getObjCPersonality(target, langOpts, cgOpts);
175 }
176 return langOpts.CPlusPlus ? getCXXPersonality(target, cgOpts)
177 : getCPersonality(target, cgOpts);
178}
179
181 const auto *fg = cgf.curCodeDecl;
182 // For outlined finallys and filters, use the SEH personality in case they
183 // contain more SEH. This mostly only affects finallys. Filters could
184 // hypothetically use gnu statement expressions to sneak in nested SEH.
185 fg = fg ? fg : cgf.curSEHParent.getDecl();
186 return get(cgf.cgm, dyn_cast_or_null<FunctionDecl>(fg));
187}
188
189static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
190 const EHPersonality &personality) {
191 // Create the personality function type: i32 (...)
192 mlir::Type i32Ty = cgm.getBuilder().getI32Type();
193 auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
194
195 cir::FuncOp personalityFn = cgm.createRuntimeFunction(
196 funcTy, personality.personalityFn, {}, /*isLocal=*/true);
197
198 return personalityFn.getSymName();
199}
200
202 const llvm::Triple &triple = getTarget().getTriple();
203 if (cgm.getLangOpts().OpenMPIsTargetDevice &&
204 (triple.isNVPTX() || triple.isAMDGCN())) {
205 cgm.errorNYI("emitCXXThrowExpr OpenMP with NVPTX or AMDGCN Triples");
206 return;
207 }
208
209 if (const Expr *subExpr = e->getSubExpr()) {
210 QualType throwType = subExpr->getType();
211 if (throwType->isObjCObjectPointerType()) {
212 cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
213 return;
214 }
215
216 cgm.getCXXABI().emitThrow(*this, e);
217 return;
218 }
219
220 cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
221}
222
224 // Make sure the exception object is cleaned up if there's an
225 // exception during initialization.
227
228 // __cxa_allocate_exception returns a void*; we need to cast this
229 // to the appropriate type for the object.
230 mlir::Type ty = convertTypeForMem(e->getType());
231 Address typedAddr = addr.withElementType(builder, ty);
232
233 // From LLVM's codegen:
234 // FIXME: this isn't quite right! If there's a final unelided call
235 // to a copy constructor, then according to [except.terminate]p1 we
236 // must call std::terminate() if that constructor throws, because
237 // technically that copy occurs after the exception expression is
238 // evaluated but before the exception is caught. But the best way
239 // to handle that is to teach EmitAggExpr to do the final copy
240 // differently if it can't be elided.
241 emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
242 /*isInitializer=*/true);
243
244 // Deactivate the cleanup block.
246}
247
249 const CXXCatchStmt *catchStmt, SmallVector<mlir::Attribute> &handlerAttrs) {
250 mlir::Location catchLoc = getLoc(catchStmt->getBeginLoc());
251
252 if (catchStmt->getExceptionDecl()) {
253 // FIXME: Dropping the reference type on the type into makes it
254 // impossible to correctly implement catch-by-reference
255 // semantics for pointers. Unfortunately, this is what all
256 // existing compilers do, and it's not clear that the standard
257 // personality routine is capable of doing this right. See C++ DR 388:
258 // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
259 Qualifiers caughtTypeQuals;
260 QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
261 catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
262 if (caughtType->isObjCObjectPointerType()) {
263 cgm.errorNYI("addCatchHandlerAttr: caughtType ObjCObjectPointerType");
264 return;
265 }
266
267 CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
268 catchLoc, caughtType, catchStmt->getCaughtType());
269 handlerAttrs.push_back(typeInfo.rtti);
270 } else {
271 // No exception decl indicates '...', a catch-all.
272 handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
273 }
274}
275
276mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
277 if (s.getTryBlock()->body_empty())
278 return mlir::LogicalResult::success();
279
280 mlir::Location loc = getLoc(s.getSourceRange());
281 // Create a scope to hold try local storage for catch params.
282
283 mlir::OpBuilder::InsertPoint scopeIP;
284 cir::ScopeOp::create(
285 builder, loc,
286 /*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
287 scopeIP = builder.saveInsertionPoint();
288 });
289
290 // Set personality function if not already set
291 auto funcOp = mlir::cast<cir::FuncOp>(curFn);
292 if (!funcOp.getPersonality())
293 funcOp.setPersonality(getPersonalityFn(cgm, EHPersonality::get(*this)));
294
295 mlir::OpBuilder::InsertionGuard guard(builder);
296 builder.restoreInsertionPoint(scopeIP);
297
298 const llvm::Triple &t = getTarget().getTriple();
299 // If we encounter a try statement on in an OpenMP target region offloaded to
300 // a GPU, we treat it as a basic block.
301 const bool isTargetDevice =
302 (cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
303 if (isTargetDevice) {
304 cgm.errorNYI("emitCXXTryStmt: OpenMP target region offloaded to GPU");
305 return mlir::success();
306 }
307
308 mlir::Location tryLoc = getLoc(s.getBeginLoc());
309 SmallVector<mlir::Attribute> handlerAttrs;
310
311 CIRGenFunction::LexicalScope tryBodyScope{*this, tryLoc,
312 builder.getInsertionBlock()};
313
314 if (getLangOpts().EHAsynch) {
315 cgm.errorNYI("enterCXXTryStmt: EHAsynch");
316 return mlir::failure();
317 }
318
319 // Create the try operation.
320 mlir::LogicalResult tryRes = mlir::success();
321 auto tryOp = cir::TryOp::create(
322 builder, tryLoc,
323 /*tryBuilder=*/
324 [&](mlir::OpBuilder &b, mlir::Location loc) {
325 // Create a RunCleanupsScope that allows us to apply any cleanups that
326 // are created for statements within the try body before exiting the
327 // try body.
328 RunCleanupsScope tryBodyCleanups(*this);
329 if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
330 tryRes = mlir::failure();
331 tryBodyCleanups.forceCleanup();
332 cir::YieldOp::create(builder, loc);
333 },
334 /*handlersBuilder=*/
335 [&](mlir::OpBuilder &b, mlir::Location loc,
336 mlir::OperationState &result) {
337 mlir::OpBuilder::InsertionGuard guard(b);
338 bool hasCatchAll = false;
339 unsigned numHandlers = s.getNumHandlers();
340 mlir::Type ehTokenTy = cir::EhTokenType::get(&getMLIRContext());
341 for (unsigned i = 0; i != numHandlers; ++i) {
342 const CXXCatchStmt *catchStmt = s.getHandler(i);
343 if (!catchStmt->getExceptionDecl())
344 hasCatchAll = true;
345 mlir::Region *region = result.addRegion();
346 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
347 addCatchHandlerAttr(catchStmt, handlerAttrs);
348 }
349 if (!hasCatchAll) {
350 // Create unwind region.
351 mlir::Region *region = result.addRegion();
352 mlir::Block *unwindBlock =
353 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
354 cir::ResumeOp::create(builder, loc, unwindBlock->getArgument(0));
355 handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
356 }
357 });
358
359 if (tryRes.failed())
360 return mlir::failure();
361
362 // Add final array of clauses into TryOp.
363 tryOp.setHandlerTypesAttr(
364 mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
365
366 // Emit the catch handler bodies. This has to be done after the try op is
367 // created and in place so that we can find the insertion point for the
368 // catch parameter alloca.
369 unsigned numHandlers = s.getNumHandlers();
370 for (unsigned i = 0; i != numHandlers; ++i) {
371 const CXXCatchStmt *catchStmt = s.getHandler(i);
372 mlir::Region *handler = &tryOp.getHandlerRegions()[i];
373 mlir::Location handlerLoc = getLoc(catchStmt->getCatchLoc());
374
375 mlir::OpBuilder::InsertionGuard guard(builder);
376 builder.setInsertionPointToStart(&handler->front());
377
378 // Get the !cir.eh_token block argument from the handler region.
379 mlir::Value ehToken = handler->front().getArgument(0);
380
381 // Enter a cleanup scope, including the catch variable and the
382 // end-catch.
383 RunCleanupsScope handlerScope(*this);
384
385 // Initialize the catch variable.
386 // TODO(cir): Move this out of CXXABI.
388 cgm.getCXXABI().emitBeginCatch(*this, catchStmt, ehToken);
389
390 // Emit the PGO counter increment.
392
393 // Perform the body of the catch.
394 [[maybe_unused]] mlir::LogicalResult emitResult =
395 emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
396 assert(emitResult.succeeded() && "failed to emit catch handler block");
397
398 // [except.handle]p11:
399 // The currently handled exception is rethrown if control
400 // reaches the end of a handler of the function-try-block of a
401 // constructor or destructor.
402
403 // TODO(cir): Handle implicit rethrow?
404
405 // Fall out through the catch cleanups.
406 handlerScope.forceCleanup();
407
408 mlir::Block *block = &handler->getBlocks().back();
409 if (block->empty() ||
410 !block->back().hasTrait<mlir::OpTrait::IsTerminator>()) {
411 mlir::OpBuilder::InsertionGuard guard(builder);
412 builder.setInsertionPointToEnd(block);
413 builder.createYield(handlerLoc);
414 }
415 }
416
417 return mlir::success();
418}
419
420// in classic codegen this function is mapping to `isInvokeDest` previously and
421// currently it's mapping to the conditions that performs early returns in
422// `getInvokeDestImpl`, in CIR we need the condition to know if the EH scope may
423// throw exception or now.
425 // If exceptions are disabled/ignored and SEH is not in use, then there is no
426 // invoke destination. SEH "works" even if exceptions are off. In practice,
427 // this means that C++ destructors and other EH cleanups don't run, which is
428 // consistent with MSVC's behavior, except in the presence of -EHa
429 const LangOptions &lo = cgm.getLangOpts();
430 if (!lo.Exceptions || lo.IgnoreExceptions) {
431 if (!lo.Borland && !lo.MicrosoftExt)
432 return false;
433 cgm.errorNYI("isInvokeDest: no exceptions or ignore exception");
434 return false;
435 }
436
437 // CUDA device code doesn't have exceptions.
438 if (lo.CUDA && lo.CUDAIsDevice)
439 return false;
440
441 return ehStack.requiresCatchOrCleanup();
442}
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 llvm::StringRef getPersonalityFn(CIRGenModule &cgm, const EHPersonality &personality)
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.
Enters a new scope for capturing cleanups, all of which will be executed once the scope is exited.
void forceCleanup(ArrayRef< mlir::Value * > valuesToReload={})
Force the emission of cleanups now, instead of waiting until this object is destroyed.
const clang::LangOptions & getLangOpts() const
const TargetInfo & getTarget() const
void addCatchHandlerAttr(const CXXCatchStmt *catchStmt, SmallVector< mlir::Attribute > &handlerAttrs)
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::Operation * curFn
The current function or global initializer that is generated code for.
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::MLIRContext & getMLIRContext()
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.
CIRGenBuilderTy & getBuilder()
const clang::TargetInfo & getTarget() const
cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name, mlir::NamedAttrList extraAttrs={}, bool isLocal=false, bool assumeConvergent=false)
const clang::CodeGenOptions & getCodeGenOpts() const
const clang::LangOptions & getLangOpts() const
CXXCatchStmt - This represents a C++ catch block.
Definition StmtCXX.h:28
SourceLocation getCatchLoc() const
Definition StmtCXX.h:48
Stmt * getHandlerBlock() const
Definition StmtCXX.h:51
SourceLocation getBeginLoc() const LLVM_READONLY
Definition StmtCXX.h:43
VarDecl * getExceptionDecl() const
Definition StmtCXX.h:49
QualType getCaughtType() const
Definition StmtCXX.cpp:19
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:8428
QualType getNonReferenceType() const
If Type is a reference type (e.g., const int&), returns the type that the reference refers to ("const...
Definition TypeBase.h:8573
The collection of all-type qualifiers we support.
Definition TypeBase.h:331
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:8804
The JSON file list parser is used to communicate input to InstallAPI.
static bool ehCleanupScope()
static bool currentFuncletPad()
static bool incrementProfileCounter()
Represents a scope, including function bodies, compound statements, and the substatements of if/while...
The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the type of a catch handler,...
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