clang 24.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
20#include "llvm/Support/SaveAndRestore.h"
21
22using namespace clang;
23using namespace clang::CIRGen;
24
25namespace clang::CIRGen {
26
28 const FunctionDecl *fd) {
30 cgm.getCodeGenOpts(), fd);
31}
32
34 const auto *fg = cgf.curCodeDecl;
35 // For outlined finallys and filters, use the SEH personality in case they
36 // contain more SEH. This mostly only affects finallys. Filters could
37 // hypothetically use gnu statement expressions to sneak in nested SEH.
38 fg = fg ? fg : cgf.curSEHParent.getDecl();
39 return getEHPersonality(cgf.cgm, dyn_cast_or_null<FunctionDecl>(fg));
40}
41
42} // namespace clang::CIRGen
43
44static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
45 const EHPersonality &personality) {
46 // Create the personality function type: i32 (...)
47 mlir::Type i32Ty = cgm.getBuilder().getSInt32Ty();
48 auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
49
50 cir::FuncOp personalityFn = cgm.createRuntimeFunction(
51 funcTy, personality.PersonalityFn, {}, /*isLocal=*/true);
52
53 return personalityFn.getSymName();
54}
55
57 if (!cgm.getLangOpts().CXXExceptions)
58 return;
59
60 const FunctionDecl *fd = dyn_cast_or_null<FunctionDecl>(d);
61 if (!fd) {
62 // This can't currently happen in CIR, but if we do get here, we need to
63 // handle the cd->isNoThrow() case.
64 if (const CapturedDecl *cd = dyn_cast_or_null<CapturedDecl>(d)) {
65 if (cd->isNothrow())
66 cgm.errorNYI(cd->getSourceRange(),
67 "emitStartEHSpec CapturedDecl nothrow");
68 }
69 return;
70 }
71
72 const FunctionProtoType *proto = fd->getType()->getAs<FunctionProtoType>();
73 if (!proto)
74 return;
75
77 // In C++17 and later, and in Wasm EH in any standard, 'throw()' aka
78 // EST_DynamicNone is treated the same way as noexcept. In earlier standards
79 // it is handled with 'throw(X...)'.
80 bool isDynamicSpec = est == EST_Dynamic ||
81 (est == EST_DynamicNone && !getLangOpts().CPlusPlus17 &&
82 !cgm.getCodeGenOpts().hasWasmExceptions());
83
84 // A specification that permits nothing to escape is a terminate scope: any
85 // exception that tries to leave the function calls std::terminate. Under
86 // -EHa a hardware exception can still occur, so there is no scope.
87 bool needsTerminate = !isDynamicSpec && proto->canThrow() == CT_Cannot &&
88 !getLangOpts().EHAsynch;
89
90 if (isDynamicSpec) {
91 // TODO: Revisit exception specifications for the MS ABI. There is a way
92 // to encode these in an object file but MSVC doesn't do anything with it.
93 if (getTarget().getCXXABI().isMicrosoft())
94 return;
95
96 // In Wasm EH, a specification with types is ignored with a warning for
97 // now. 'throw()' is a terminate scope, which the classification above
98 // takes care of.
99 // TODO Correctly handle exception specification in Wasm EH
100 if (cgm.getCodeGenOpts().hasWasmExceptions()) {
101 cgm.getDiags().Report(d->getLocation(),
102 diag::warn_wasm_dynamic_exception_spec_ignored)
104 return;
105 }
106
107 // Currently Emscripten EH only handles 'throw()' but not 'throw' with
108 // types. 'throw()' handling will be done in JS glue code so we don't need
109 // to do anything in that case. Just print a warning message in case of
110 // throw with types.
111 // TODO Correctly handle exception specification in Emscripten EH
112 if (getTarget().getCXXABI() == TargetCXXABI::WebAssembly &&
113 (cgm.getCodeGenOpts().getExceptionHandling() ==
115 cgm.getCodeGenOpts().getExceptionHandling() ==
117 est == EST_Dynamic)
118 cgm.getDiags().Report(d->getLocation(),
119 diag::warn_wasm_dynamic_exception_spec_ignored)
121 } else if (!needsTerminate) {
122 return;
123 }
124
125 mlir::Location loc = getLoc(fd->getSourceRange());
126
127 SmallVector<mlir::Attribute, 4> permittedTypes;
128 if (isDynamicSpec) {
129 for (QualType ty : proto->exceptions()) {
131 permittedTypes.push_back(
132 cgm.getAddrOfRTTIDescriptor(loc, exceptType, /*forEh=*/true));
133 }
134 }
135
136 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(curFn);
137 if (!funcOp.getPersonality())
138 funcOp.setPersonality(getPersonalityFn(cgm, getEHPersonality(*this)));
139
140 bool emptyFilter = permittedTypes.empty();
141 ehSpecTryOp = cir::TryOp::create(
142 builder, loc,
143 // The try body holds the function body, which the caller emits after
144 // this function returns and emitEndEHSpec terminates.
145 /*tryBuilder=*/[](mlir::OpBuilder &, mlir::Location) {},
146 /*handlersBuilder=*/
147 [&](mlir::OpBuilder &b, mlir::Location loc,
148 mlir::OperationState &result) {
149 mlir::OpBuilder::InsertionGuard guard(b);
150 mlir::Type ehTokenTy = cir::EhTokenType::get(&getMLIRContext());
151
152 if (needsTerminate) {
153 mlir::Block *terminateBlock = b.createBlock(
154 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
155 cir::EhTerminateOp::create(b, loc, terminateBlock->getArgument(0));
156 return;
157 }
158
159 mlir::Block *filterBlock = b.createBlock(
160 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
161 if (emptyFilter)
162 cir::UnreachableOp::create(b, loc);
163 else
164 cir::ResumeOp::create(b, loc, filterBlock->getArgument(0));
165
166 mlir::Block *unexpectedBlock = b.createBlock(
167 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
168 cir::EhUnexpectedOp::create(b, loc, unexpectedBlock->getArgument(0));
169 });
170
172 if (needsTerminate) {
173 // Any exception reaching the handler terminates the program, so it catches
174 // everything. The catch itself is performed by the runtime helper that
175 // cir.eh.terminate lowers to.
176 handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
177 } else {
178 handlerAttrs.push_back(cir::EhFilterAttr::get(
179 &getMLIRContext(), builder.getArrayAttr(permittedTypes)));
180 handlerAttrs.push_back(cir::EhUnexpectedAttr::get(&getMLIRContext()));
181 }
182 ehSpecTryOp.setHandlerTypesAttr(
183 mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
184
185 // Continue emitting into the try body.
186 builder.setInsertionPointToEnd(&ehSpecTryOp.getTryRegion().front());
187}
188
190 if (!ehSpecTryOp)
191 return;
192
193 cir::TryOp tryOp = ehSpecTryOp;
194 ehSpecTryOp = cir::TryOp();
195
196 // Terminate the try body. Emitting the function body may have left the last
197 // block without a terminator, for instance when control falls off the end.
198 mlir::Block *bodyExit = &tryOp.getTryRegion().back();
199 if (bodyExit->empty() ||
200 !bodyExit->back().hasTrait<mlir::OpTrait::IsTerminator>()) {
201 mlir::OpBuilder::InsertionGuard guard(builder);
202 builder.setInsertionPointToEnd(bodyExit);
203 builder.createYield(tryOp.getLoc());
204 }
205
206 // Continue emitting the function epilogue outside the try.
207 builder.setInsertionPointAfter(tryOp);
208}
209
211 const llvm::Triple &triple = getTarget().getTriple();
212 if (cgm.getLangOpts().OpenMPIsTargetDevice &&
213 (triple.isNVPTX() || triple.isAMDGCN())) {
214 cgm.errorNYI("emitCXXThrowExpr OpenMP with NVPTX or AMDGCN Triples");
215 return;
216 }
217
218 if (const Expr *subExpr = e->getSubExpr()) {
219 QualType throwType = subExpr->getType();
220 if (throwType->isObjCObjectPointerType()) {
221 cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
222 return;
223 }
224
225 cgm.getCXXABI().emitThrow(*this, e);
226 return;
227 }
228
229 cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
230}
231
233 // Make sure the exception object is cleaned up if there's an
234 // exception during initialization.
236
237 // __cxa_allocate_exception returns a void*; we need to cast this
238 // to the appropriate type for the object.
239 mlir::Type ty = convertTypeForMem(e->getType());
240 Address typedAddr = addr.withElementType(builder, ty);
241
242 // From LLVM's codegen:
243 // FIXME: this isn't quite right! If there's a final unelided call
244 // to a copy constructor, then according to [except.terminate]p1 we
245 // must call std::terminate() if that constructor throws, because
246 // technically that copy occurs after the exception expression is
247 // evaluated but before the exception is caught. But the best way
248 // to handle that is to teach EmitAggExpr to do the final copy
249 // differently if it can't be elided.
250 emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
251 /*isInitializer=*/true);
252
253 // Deactivate the cleanup block.
255}
256
258 const CXXCatchStmt *catchStmt, SmallVector<mlir::Attribute> &handlerAttrs) {
259 mlir::Location catchLoc = getLoc(catchStmt->getBeginLoc());
260
261 if (catchStmt->getExceptionDecl()) {
262 // FIXME: Dropping the reference type on the type into makes it
263 // impossible to correctly implement catch-by-reference
264 // semantics for pointers. Unfortunately, this is what all
265 // existing compilers do, and it's not clear that the standard
266 // personality routine is capable of doing this right. See C++ DR 388:
267 // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
268 Qualifiers caughtTypeQuals;
269 QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
270 catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
271 if (caughtType->isObjCObjectPointerType()) {
272 cgm.errorNYI("addCatchHandlerAttr: caughtType ObjCObjectPointerType");
273 return;
274 }
275
276 CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
277 catchLoc, caughtType, catchStmt->getCaughtType());
278 handlerAttrs.push_back(typeInfo.rtti);
279 } else {
280 // No exception decl indicates '...', a catch-all.
281 handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
282 }
283}
284
285namespace {
286struct CallEndCatch final : EHScopeStack::Cleanup {
287 CallEndCatch(mlir::Value catchToken) : catchToken(catchToken) {}
288 mlir::Value catchToken;
289
290 void emit(CIRGenFunction &cgf, Flags flags) override {
291 cir::EndCatchOp::create(cgf.getBuilder(), cgf.getLoc(*cgf.currSrcLoc),
292 catchToken);
293 cir::YieldOp::create(cgf.getBuilder(), cgf.getLoc(*cgf.currSrcLoc));
294 }
295};
296} // namespace
297
298static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Value ehToken,
299 mlir::Type exnPtrTy) {
300 auto catchTokenTy = cir::CatchTokenType::get(cgf.getBuilder().getContext());
301 auto beginCatch = cir::BeginCatchOp::create(cgf.getBuilder(),
302 cgf.getBuilder().getUnknownLoc(),
303 catchTokenTy, exnPtrTy, ehToken);
304
305 cgf.ehStack.pushCleanup<CallEndCatch>(NormalAndEHCleanup,
306 beginCatch.getCatchToken());
307
308 return beginCatch.getExnPtr();
309}
310
311/// Get or create the catch-init copy thunk for \p catchParam.
312///
313/// The copy thunk has signature `void(T*, T*)` (where `T` is the catch
314/// parameter type) and contains the normal aggregate emission of the catch
315/// parameter's init expression.
316///
317/// The thunk name is keyed off the catch parameter's canonical type mangled
318/// name, so a single translation unit emits at most one thunk per catch type.
319static cir::FuncOp getOrCreateCopyThunk(CIRGenFunction &cgf,
320 const VarDecl &catchParam,
321 cir::PointerType paramAddrType,
322 SourceLocation clangLoc) {
323 mlir::Location loc = cgf.getLoc(clangLoc);
324 CIRGenModule &cgm = cgf.cgm;
325 CIRGenBuilderTy &builder = cgm.getBuilder();
326 mlir::ModuleOp mod = cgm.getModule();
327
328 const Expr *copyExpr = catchParam.getInit();
329 assert(copyExpr && "non-trivial copy expects a copy expression");
330
331 llvm::SmallString<128> thunkName;
332 llvm::raw_svector_ostream thunkNameStream(thunkName);
333 thunkNameStream << "__clang_cir_catch_copy_";
335 catchParam.getType(), thunkNameStream);
336
337 if (cir::FuncOp existing = cgm.lookupFuncOp(thunkName))
338 return existing;
339
340 mlir::Type voidTy = cir::VoidType::get(builder.getContext());
341 auto thunkTy = cir::FuncType::get({paramAddrType, paramAddrType}, voidTy,
342 /*isVarArg=*/false);
343
344 mlir::OpBuilder::InsertionGuard guard(builder);
345 builder.setInsertionPointToEnd(mod.getBody());
346 cir::FuncOp thunk = cir::FuncOp::create(builder, loc, thunkName, thunkTy);
347 cgm.insertGlobalSymbol(thunk);
348 thunk.setLinkage(cir::GlobalLinkageKind::LinkOnceODRLinkage);
349 thunk.setGlobalVisibility(cir::VisibilityKind::Hidden);
350 thunk->setAttr(cir::CIRDialect::getCatchCopyThunkAttrName(),
351 builder.getUnitAttr());
352
353 mlir::Block *entry = thunk.addEntryBlock();
354 builder.setInsertionPointToStart(entry);
355
356 // Use a fresh CIRGenFunction to drive the body emission. We need just enough
357 // state for emitAggExpr / emitCXXConstructorCall to compute the call-site
358 // argument attributes; the helper has no AST decl, no exception scopes, and
359 // no return value, so we bypass the full startFunction/finishFunction
360 // machinery.
361 CIRGenFunction subCgf(cgm, builder);
362 subCgf.curFn = thunk;
363
364 // Some emission paths (e.g. materializing temporaries for default args via
365 // emitAnyExprToTemp) need both a current source location and a lexical
366 // scope to anchor allocas. Since we bypass startFunction, install both
367 // explicitly for the lifetime of the thunk's body emission.
368 CIRGenFunction::SourceLocRAIIObject thunkLoc(subCgf, clangLoc);
369 CIRGenFunction::LexicalScope thunkScope(subCgf, loc, entry);
370
371 // Bind the OpaqueValueExpr at the source position of the catch parameter's
372 // copy expression to an LValue at the thunk's `src` block argument.
373 LValue srcLV = subCgf.makeNaturalAlignAddrLValue(entry->getArgument(1),
374 catchParam.getType());
376 subCgf, OpaqueValueExpr::findInCopyConstruct(copyExpr), srcLV);
377
378 // Drive the construction into the helper's `dest` block argument via the
379 // normal aggregate-emission machinery so that `ExprWithCleanups`,
380 // converting/inheriting constructors, and any future copy-construction
381 // shapes flow through unchanged.
382 Address destAddr = subCgf.makeNaturalAddressForPointer(
383 entry->getArgument(0), catchParam.getType(), clang::CharUnits::Zero());
384 subCgf.emitAggExpr(
385 copyExpr, AggValueSlot::forAddr(
388
389 cir::ReturnOp::create(builder, loc);
390 return thunk;
391}
392
393/// A "special initializer" callback for initializing a catch
394/// parameter during catch initialization.
396 mlir::Value ehToken, const VarDecl &catchParam,
397 SourceLocation loc) {
398 CanQualType catchType =
399 cgf.cgm.getASTContext().getCanonicalType(catchParam.getType());
400 cir::InitCatchKind kind;
401
402 if (isa<ReferenceType>(catchType)) {
403 QualType caughtType = cast<ReferenceType>(catchType)->getPointeeType();
404 const PointerType *ptr = dyn_cast<PointerType>(caughtType);
405 if (!ptr)
406 kind = cir::InitCatchKind::Reference;
407 else if (ptr->getPointeeType()->isRecordType())
408 kind = cir::InitCatchKind::ReferenceToRecordPointer;
409 else
410 kind = cir::InitCatchKind::ReferenceToPointer;
411 } else {
412 cir::TypeEvaluationKind tek = cgf.getEvaluationKind(catchType);
413 if (tek == cir::TEK_Aggregate) {
414 assert(isa<RecordType>(catchType) && "unexpected catch type!");
415 const Expr *copyExpr = catchParam.getInit();
416 kind = !copyExpr ? cir::InitCatchKind::TrivialCopy
417 : cir::InitCatchKind::NonTrivialCopy;
418 } else {
419 // Scalars and complexes.
420 if (catchType->hasPointerRepresentation()) {
421 switch (catchType.getQualifiers().getObjCLifetime()) {
424 kind = cir::InitCatchKind::Objc;
425 break;
426
430 kind = cir::InitCatchKind::Pointer;
431 break;
432 }
433 } else {
434 kind = cir::InitCatchKind::Scalar;
435 }
436 }
437 }
438
440 Address paramAddr = var.getAllocatedAddress();
441 mlir::Location mloc = cgf.getLoc(loc);
442
443 if (kind == cir::InitCatchKind::NonTrivialCopy ||
444 kind == cir::InitCatchKind::ReferenceToPointer) {
445 // Sanitizer-checked construction (UBSan vptr/derived-class checks, etc.)
446 // would require additional adornments that cir.construct_catch_param does
447 // not yet carry.
449
450 mlir::FlatSymbolRefAttr copyFun{};
451 if (kind == cir::InitCatchKind::NonTrivialCopy) {
452 auto paramAddrType =
453 mlir::cast<cir::PointerType>(paramAddr.getPointer().getType());
454 cir::FuncOp thunk =
455 getOrCreateCopyThunk(cgf, catchParam, paramAddrType, loc);
456 copyFun = mlir::FlatSymbolRefAttr::get(thunk.getSymNameAttr());
457 }
458
459 cir::ConstructCatchParamOp::create(builder, mloc, ehToken,
460 paramAddr.getPointer(), kind, copyFun);
461 }
462
463 mlir::Value exnPtr = callBeginCatch(cgf, ehToken, builder.getVoidPtrTy());
464 cir::InitCatchParamOp::create(builder, mloc, exnPtr, paramAddr.getPointer(),
465 kind);
466 cgf.emitAutoVarCleanups(var);
467}
468
469/// Begins a catch statement by initializing the catch variable and
470/// calling __cxa_begin_catch.
472 mlir::Value ehToken) {
473 // We have to be very careful with the ordering of cleanups here:
474 // C++ [except.throw]p4:
475 // The destruction [of the exception temporary] occurs
476 // immediately after the destruction of the object declared in
477 // the exception-declaration in the handler.
478 //
479 // So the precise ordering is:
480 // 1. Construct catch variable.
481 // 2. begin_catch
482 // 3. Enter CallEndCatch cleanup
483 // 4. Enter dtor cleanup
484 //
485 VarDecl *catchParam = catchStmt->getExceptionDecl();
486 if (!catchParam) {
487 callBeginCatch(*this, ehToken, builder.getVoidPtrTy());
488 return;
489 }
490
491 // Emit the local. Make sure the alloca's superseed the current scope, since
492 // these are going to be consumed by `cir.catch`, which is not within the
493 // current scope.
494 initCatchParam(*this, builder, ehToken, *catchParam,
495 catchStmt->getBeginLoc());
496}
497
499 const CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback, bool isFnTryBlock) {
500 mlir::Location loc = getLoc(s.getSourceRange());
501
502 // Create a scope to hold try local storage for catch params.
503 mlir::OpBuilder::InsertPoint scopeIP;
504 cir::ScopeOp::create(
505 builder, loc,
506 /*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
507 scopeIP = builder.saveInsertionPoint();
508 });
509
510 // Set personality function if not already set
511 auto funcOp = mlir::cast<cir::FuncOp>(curFn);
512 if (!funcOp.getPersonality())
513 funcOp.setPersonality(getPersonalityFn(cgm, getEHPersonality(*this)));
514
515 mlir::OpBuilder::InsertionGuard guard(builder);
516 builder.restoreInsertionPoint(scopeIP);
517
518 const llvm::Triple &t = getTarget().getTriple();
519 // If we encounter a try statement on in an OpenMP target region offloaded
520 // to a GPU, we treat it as a basic block.
521 const bool isTargetDevice =
522 (cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
523 if (isTargetDevice) {
524 cgm.errorNYI("emitCXXTryStmt: OpenMP target region offloaded to GPU");
525 return mlir::success();
526 }
527
528 mlir::Location tryLoc = getLoc(s.getBeginLoc());
529 SmallVector<mlir::Attribute> handlerAttrs;
530
531 CIRGenFunction::LexicalScope tryBodyScope{*this, tryLoc,
532 builder.getInsertionBlock()};
533
534 if (getLangOpts().EHAsynch) {
535 cgm.errorNYI("enterCXXTryStmt: EHAsynch");
536 return mlir::failure();
537 }
538
539 // Create the try operation.
540 mlir::LogicalResult tryRes = mlir::success();
541 auto tryOp = cir::TryOp::create(
542 builder, tryLoc,
543 /*tryBuilder=*/
544 [&](mlir::OpBuilder &b, mlir::Location loc) {
545 // Create a RunCleanupsScope that allows us to apply any cleanups that
546 // are created for statements within the try body before exiting the
547 // try body.
548 RunCleanupsScope tryBodyCleanups(*this);
549 if (bodyCallback(*this).failed())
550 tryRes = mlir::failure();
551 tryBodyCleanups.forceCleanup();
552 if (!builder.getBlock()->mightHaveTerminator() ||
553 !builder.getBlock()->getTerminator())
554 cir::YieldOp::create(builder, loc);
555 },
556 /*handlersBuilder=*/
557 [&](mlir::OpBuilder &b, mlir::Location loc,
558 mlir::OperationState &result) {
559 mlir::OpBuilder::InsertionGuard guard(b);
560 bool hasCatchAll = false;
561 unsigned numHandlers = s.getNumHandlers();
562 mlir::Type ehTokenTy = cir::EhTokenType::get(&getMLIRContext());
563 for (unsigned i = 0; i != numHandlers; ++i) {
564 const CXXCatchStmt *catchStmt = s.getHandler(i);
565 if (!catchStmt->getExceptionDecl())
566 hasCatchAll = true;
567 mlir::Region *region = result.addRegion();
568 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
569 addCatchHandlerAttr(catchStmt, handlerAttrs);
570 }
571 if (!hasCatchAll) {
572 // Create unwind region.
573 mlir::Region *region = result.addRegion();
574 mlir::Block *unwindBlock =
575 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
576 cir::ResumeOp::create(builder, loc, unwindBlock->getArgument(0));
577 handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
578 }
579 });
580
581 if (tryRes.failed())
582 return mlir::failure();
583
584 // Add final array of clauses into TryOp.
585 tryOp.setHandlerTypesAttr(
586 mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
587
588 // Determine if we need an implicit rethrow for all these catch handlers;
589 // see the comment below.
590 const bool doImplicitRethrow =
591 isFnTryBlock && (isa<CXXConstructorDecl>(curCodeDecl) ||
593
594 // Emit the catch handler bodies. This has to be done after the try op is
595 // created and in place so that we can find the insertion point for the
596 // catch parameter alloca.
597 unsigned numHandlers = s.getNumHandlers();
598 for (unsigned i = 0; i != numHandlers; ++i) {
599 const CXXCatchStmt *catchStmt = s.getHandler(i);
600 mlir::Region *handler = &tryOp.getHandlerRegions()[i];
601 mlir::Location handlerLoc = getLoc(catchStmt->getCatchLoc());
602
603 mlir::OpBuilder::InsertionGuard guard(builder);
604 builder.setInsertionPointToStart(&handler->front());
605
606 // Get the !cir.eh_token block argument from the handler region.
607 mlir::Value ehToken = handler->front().getArgument(0);
608
609 // Enter a cleanup scope, including the catch variable and the
610 // end-catch.
611 RunCleanupsScope handlerScope(*this);
612
613 // Initialize the catch variable.
614 // TODO(cir): Move this out of CXXABI.
616 emitBeginCatch(catchStmt, ehToken);
617
618 // Emit the PGO counter increment.
620
621 // Perform the body of the catch.
622 [[maybe_unused]] mlir::LogicalResult emitResult =
623 emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
624 assert(emitResult.succeeded() && "failed to emit catch handler block");
625
626 // [except.handle]p11:
627 // The currently handled exception is rethrown if control
628 // reaches the end of a handler of the function-try-block of a
629 // constructor or destructor.
630 //
631 // It is important that we only do this on fallthrough and not on
632 // return. Note that it's illegal to put a return in a
633 // constructor function-try-block's catch handler (p14), so this
634 // really only applies to destructors.
635 if (doImplicitRethrow && insertionPointIsReachable())
636 cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
637
638 // Fall out through the catch cleanups.
639 handlerScope.forceCleanup();
640
641 mlir::Block *block = &handler->getBlocks().back();
642 if (block->empty() ||
643 !block->back().hasTrait<mlir::OpTrait::IsTerminator>()) {
644 mlir::OpBuilder::InsertionGuard guard(builder);
645 builder.setInsertionPointToEnd(block);
646 builder.createYield(handlerLoc);
647 }
648 }
649
650 return mlir::success();
651}
652
653mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
654 if (s.getTryBlock()->body_empty())
655 return mlir::LogicalResult::success();
656
657 struct simpleTryBodyEmitter final : cxxTryBodyEmitter {
658 const clang::CXXTryStmt &s;
659 simpleTryBodyEmitter(const clang::CXXTryStmt &s) : s(s) {}
660
661 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
662 return cgf.emitStmt(s.getTryBlock(), /*useCurrentScope=*/true);
663 }
664 ~simpleTryBodyEmitter() override = default;
665 };
666
667 simpleTryBodyEmitter emitter{s};
668
669 return emitCXXTryStmt(s, emitter);
670}
671
672// in classic codegen this function is mapping to `isInvokeDest` previously
673// and currently it's mapping to the conditions that performs early returns in
674// `getInvokeDestImpl`, in CIR we need the condition to know if the EH scope
675// may throw exception or now.
677 // If exceptions are disabled/ignored and SEH is not in use, then there is
678 // no invoke destination. SEH "works" even if exceptions are off. In
679 // practice, this means that C++ destructors and other EH cleanups don't
680 // run, which is consistent with MSVC's behavior, except in the presence of
681 // -EHa
682 const LangOptions &lo = cgm.getLangOpts();
683 if (!lo.Exceptions || lo.IgnoreExceptions) {
684 if (!lo.Borland && !lo.MicrosoftExt)
685 return false;
686 cgm.errorNYI("isInvokeDest: no exceptions or ignore exception");
687 return false;
688 }
689
690 // CUDA device code doesn't have exceptions.
691 if (lo.CUDA && lo.CUDAIsDevice)
692 return false;
693
694 return ehStack.requiresCatchOrCleanup();
695}
static void emit(Program &P, llvm::SmallVectorImpl< std::byte > &Code, const T &Val, bool &Success)
Helper to write bytecode and bail out if 32-bit offsets become invalid.
static cir::FuncOp getOrCreateCopyThunk(CIRGenFunction &cgf, const VarDecl &catchParam, cir::PointerType paramAddrType, SourceLocation clangLoc)
Get or create the catch-init copy thunk for catchParam.
static llvm::StringRef getPersonalityFn(CIRGenModule &cgm, const EHPersonality &personality)
static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Value ehToken, mlir::Type exnPtrTy)
static void initCatchParam(CIRGenFunction &cgf, CIRGenBuilderTy &builder, mlir::Value ehToken, const VarDecl &catchParam, SourceLocation loc)
A "special initializer" callback for initializing a catch parameter during catch initialization.
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
cir::PointerType getVoidPtrTy(clang::LangAS langAS=clang::LangAS::Default)
static CanQualType getCanonicalType(QualType T)
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
mlir::Value getPointer() const
Definition Address.h:98
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const
Return address with different element type, a bitcast pointer, and the same alignment.
static AggValueSlot forAddr(Address addr, clang::Qualifiers quals, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed=IsNotZeroed)
clang::MangleContext & getMangleContext()
Gets the mangle context.
An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
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.
static cir::TypeEvaluationKind getEvaluationKind(clang::QualType type)
Return the cir::TypeEvaluationKind of QualType type.
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d, mlir::OpBuilder::InsertPoint ip={})
const clang::LangOptions & getLangOpts() const
const TargetInfo & getTarget() const
void addCatchHandlerAttr(const CXXCatchStmt *catchStmt, SmallVector< mlir::Attribute > &handlerAttrs)
Address makeNaturalAddressForPointer(mlir::Value ptr, QualType t, CharUnits alignment, bool forPointeeType=false, LValueBaseInfo *baseInfo=nullptr)
Construct an address with the natural alignment of T.
bool insertionPointIsReachable() const
True if code emitted at the builder's insertion point can be reached.
void emitAnyExprToExn(const Expr *e, Address addr)
LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty)
cir::TryOp ehSpecTryOp
The cir.try wrapping a function whose exception specification has to be enforced.
void emitBeginCatch(const CXXCatchStmt *catchStmt, mlir::Value ehToken)
Begins a catch statement by initializing the catch variable and calling __cxa_begin_catch.
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)
mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback, bool isFnTryBlock=false)
const clang::Decl * curCodeDecl
This is the inner-most code context, which includes blocks.
CIRGenBuilderTy & getBuilder()
mlir::MLIRContext & getMLIRContext()
std::optional< SourceRange > currSrcLoc
Use to track source locations across nested visitor traversals.
void emitCXXThrowExpr(const CXXThrowExpr *e)
void emitEndEHSpec(const clang::Decl *d)
Close the cir.try opened by emitStartEHSpec.
CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext=false)
void emitStartEHSpec(const clang::Decl *d)
Wrap the function body in a cir.try that enforces the exception specification of d: a filter handler ...
mlir::LogicalResult emitStmt(const clang::Stmt *s, bool useCurrentScope, llvm::ArrayRef< const Attr * > attrs={})
void emitAggExpr(const clang::Expr *e, AggValueSlot slot)
void emitAutoVarCleanups(const AutoVarEmission &emission)
This class organizes the cross-function state that is used while generating CIR code.
clang::ASTContext & getASTContext() const
void insertGlobalSymbol(mlir::Operation *op)
CIRGenBuilderTy & getBuilder()
cir::FuncOp lookupFuncOp(llvm::StringRef name)
O(1) lookup of a FuncOp by name in the symbol cache.
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
mlir::ModuleOp getModule() const
CIRGenCXXABI & getCXXABI() const
Information for lazily generating a cleanup.
CXXCatchStmt - This represents a C++ catch block.
Definition StmtCXX.h:29
SourceLocation getCatchLoc() const
Definition StmtCXX.h:49
Stmt * getHandlerBlock() const
Definition StmtCXX.h:52
SourceLocation getBeginLoc() const LLVM_READONLY
Definition StmtCXX.h:44
VarDecl * getExceptionDecl() const
Definition StmtCXX.h:50
QualType getCaughtType() const
Definition StmtCXX.cpp:20
A C++ throw-expression (C++ [except.throw]).
Definition ExprCXX.h:1213
const Expr * getSubExpr() const
Definition ExprCXX.h:1233
CXXTryStmt - A C++ try block, including all handlers.
Definition StmtCXX.h:70
CXXCatchStmt * getHandler(unsigned i)
Definition StmtCXX.h:109
unsigned getNumHandlers() const
Definition StmtCXX.h:108
SourceLocation getBeginLoc() const LLVM_READONLY
Definition StmtCXX.h:94
CompoundStmt * getTryBlock()
Definition StmtCXX.h:101
Qualifiers getQualifiers() const
Retrieve all qualifiers.
Represents the body of a CapturedStmt, and serves as its DeclContext.
Definition Decl.h:5086
static CharUnits Zero()
Construct a CharUnits quantity of zero.
Definition CharUnits.h:52
bool body_empty() const
Definition Stmt.h:1796
SourceLocation getLocation() const
Definition DeclBase.h:447
This represents one expression.
Definition Expr.h:113
QualType getType() const
Definition Expr.h:145
Represents a function declaration or definition.
Definition Decl.h:2059
SourceRange getExceptionSpecSourceRange() const
Attempt to compute an informative source range covering the function exception specification,...
Definition Decl.cpp:4105
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:4618
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5416
ExceptionSpecificationType getExceptionSpecType() const
Get the kind of exception specification on this function.
Definition TypeBase.h:5723
CanThrowResult canThrow() const
Determine whether this function type has a non-throwing exception specification.
Definition Type.cpp:4115
ArrayRef< QualType > exceptions() const
Definition TypeBase.h:5870
const Decl * getDecl() const
Definition GlobalDecl.h:115
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
virtual void mangleCanonicalTypeName(QualType T, raw_ostream &, bool NormalizeIntegers=false)=0
Generates a unique string for an externally visible type for use with TBAA or type uniquing.
static const OpaqueValueExpr * findInCopyConstruct(const Expr *expr)
Given an expression which invokes a copy constructor — i.e.
Definition Expr.cpp:5210
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3403
QualType getPointeeType() const
Definition TypeBase.h:3413
A (possibly-)qualified type.
Definition TypeBase.h:938
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8486
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:8631
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8540
The collection of all-type qualifiers we support.
Definition TypeBase.h:332
@ OCL_Strong
Assigning into this object requires the old value to be released and the new value to be retained.
Definition TypeBase.h:362
@ OCL_ExplicitNone
This object can be modified without requiring retains or releases.
Definition TypeBase.h:355
@ OCL_None
There is no lifetime qualification on this type.
Definition TypeBase.h:351
@ OCL_Weak
Reading or writing from this object requires a barrier call.
Definition TypeBase.h:365
@ OCL_Autoreleasing
Assigning into this object requires a lifetime extension.
Definition TypeBase.h:368
ObjCLifetime getObjCLifetime() const
Definition TypeBase.h:546
Encodes a location in the source.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isObjCObjectPointerType() const
Definition TypeBase.h:8862
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9299
bool isRecordType() const
Definition TypeBase.h:8810
QualType getType() const
Definition Decl.h:724
Represents a variable declaration or definition.
Definition Decl.h:933
const Expr * getInit() const
Definition Decl.h:1392
const EHPersonality & getEHPersonality(CIRGenModule &cgm, const clang::FunctionDecl *fd)
Selects the personality function to use for fd, or for the translation unit as a whole when fd is nul...
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
const EHPersonality & getEHPersonality(const TargetInfo &Target, const LangOptions &LangOpts, const CodeGenOptions &CGOpts, const FunctionDecl *FD)
Selects the personality function to use for FD, or for the translation unit as a whole when FD is nul...
Top level wrappers for InstallAPI frontend operations.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
bool isa(CodeGen::Address addr)
Definition Address.h:330
U cast(CodeGen::Address addr)
Definition Address.h:327
ExceptionSpecificationType
The various types of exception specifications that exist in C++11.
@ EST_Dynamic
throw(T1, T2)
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.