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
25const EHPersonality EHPersonality::GNU_C = {"__gcc_personality_v0", nullptr};
26const EHPersonality EHPersonality::GNU_C_SJLJ = {"__gcc_personality_sj0",
27 nullptr};
28const EHPersonality EHPersonality::GNU_C_SEH = {"__gcc_personality_seh0",
29 nullptr};
30const EHPersonality EHPersonality::NeXT_ObjC = {"__objc_personality_v0",
31 nullptr};
32const EHPersonality EHPersonality::GNU_CPlusPlus = {"__gxx_personality_v0",
33 nullptr};
35 "__gxx_personality_sj0", nullptr};
37 "__gxx_personality_seh0", nullptr};
38const EHPersonality EHPersonality::GNU_ObjC = {"__gnu_objc_personality_v0",
39 "objc_exception_throw"};
41 "__gnu_objc_personality_sj0", "objc_exception_throw"};
43 "__gnu_objc_personality_seh0", "objc_exception_throw"};
45 "__gnustep_objcxx_personality_v0", nullptr};
47 "__gnustep_objc_personality_v0", nullptr};
48const EHPersonality EHPersonality::MSVC_except_handler = {"_except_handler3",
49 nullptr};
51 "__C_specific_handler", nullptr};
53 "__CxxFrameHandler3", nullptr};
55 "__gxx_wasm_personality_v0", nullptr};
56const EHPersonality EHPersonality::XL_CPlusPlus = {"__xlcxx_personality_v1",
57 nullptr};
58const EHPersonality EHPersonality::ZOS_CPlusPlus = {"__zos_cxx_personality_v2",
59 nullptr};
60
61static const EHPersonality &getCPersonality(const TargetInfo &target,
62 const CodeGenOptions &cgOpts) {
63 const llvm::Triple &triple = target.getTriple();
64 if (triple.isWindowsMSVCEnvironment())
66 if (cgOpts.hasSjLjExceptions())
68 if (cgOpts.hasDWARFExceptions())
70 if (cgOpts.hasSEHExceptions())
73}
74
75static const EHPersonality &getObjCPersonality(const TargetInfo &target,
76 const LangOptions &langOpts,
77 const CodeGenOptions &cgOpts) {
78 const llvm::Triple &triple = target.getTriple();
79 if (triple.isWindowsMSVCEnvironment())
81
82 switch (langOpts.ObjCRuntime.getKind()) {
84 return getCPersonality(target, cgOpts);
90 if (langOpts.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
92 [[fallthrough]];
95 if (cgOpts.hasSjLjExceptions())
97 if (cgOpts.hasSEHExceptions())
100 }
101 llvm_unreachable("bad runtime kind");
102}
103
104static const EHPersonality &getCXXPersonality(const TargetInfo &target,
105 const CodeGenOptions &cgOpts) {
106 const llvm::Triple &triple = target.getTriple();
107 if (triple.isWindowsMSVCEnvironment())
109 if (triple.isOSAIX())
111 if (cgOpts.hasSjLjExceptions())
113 if (cgOpts.hasDWARFExceptions())
115 if (cgOpts.hasSEHExceptions())
117 if (cgOpts.hasWasmExceptions())
120}
121
122/// Determines the personality function to use when both C++
123/// and Objective-C exceptions are being caught.
125 const LangOptions &langOpts,
126 const CodeGenOptions &cgOpts) {
127 if (target.getTriple().isWindowsMSVCEnvironment())
129
130 switch (langOpts.ObjCRuntime.getKind()) {
131 // In the fragile ABI, just use C++ exception handling and hope
132 // they're not doing crazy exception mixing.
134 return getCXXPersonality(target, cgOpts);
135
136 // The ObjC personality defers to the C++ personality for non-ObjC
137 // handlers. Unlike the C++ case, we use the same personality
138 // function on targets using (backend-driven) SJLJ EH.
140 case ObjCRuntime::iOS:
142 return getObjCPersonality(target, langOpts, cgOpts);
143
146
147 // The GCC runtime's personality function inherently doesn't support
148 // mixed EH. Use the ObjC personality just to avoid returning null.
149 case ObjCRuntime::GCC:
151 return getObjCPersonality(target, langOpts, cgOpts);
152 }
153 llvm_unreachable("bad runtime kind");
154}
155
156static const EHPersonality &getSEHPersonalityMSVC(const llvm::Triple &triple) {
157 return triple.getArch() == llvm::Triple::x86
160}
161
163 const FunctionDecl *fd) {
164 const llvm::Triple &triple = cgm.getTarget().getTriple();
165 const LangOptions &langOpts = cgm.getLangOpts();
166 const CodeGenOptions &cgOpts = cgm.getCodeGenOpts();
167 const TargetInfo &target = cgm.getTarget();
168
169 // Functions using SEH get an SEH personality.
170 if (fd && fd->usesSEHTry())
171 return getSEHPersonalityMSVC(triple);
172
173 if (langOpts.ObjC) {
174 return langOpts.CPlusPlus ? getObjCXXPersonality(target, langOpts, cgOpts)
175 : getObjCPersonality(target, langOpts, cgOpts);
176 }
177 return langOpts.CPlusPlus ? getCXXPersonality(target, cgOpts)
178 : getCPersonality(target, cgOpts);
179}
180
182 const auto *fg = cgf.curCodeDecl;
183 // For outlined finallys and filters, use the SEH personality in case they
184 // contain more SEH. This mostly only affects finallys. Filters could
185 // hypothetically use gnu statement expressions to sneak in nested SEH.
186 fg = fg ? fg : cgf.curSEHParent.getDecl();
187 return get(cgf.cgm, dyn_cast_or_null<FunctionDecl>(fg));
188}
189
190static llvm::StringRef getPersonalityFn(CIRGenModule &cgm,
191 const EHPersonality &personality) {
192 // Create the personality function type: i32 (...)
193 mlir::Type i32Ty = cgm.getBuilder().getSInt32Ty();
194 auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true);
195
196 cir::FuncOp personalityFn = cgm.createRuntimeFunction(
197 funcTy, personality.personalityFn, {}, /*isLocal=*/true);
198
199 return personalityFn.getSymName();
200}
201
203 if (!cgm.getLangOpts().CXXExceptions)
204 return;
205
206 const FunctionDecl *fd = dyn_cast_or_null<FunctionDecl>(d);
207 if (!fd) {
208 // This can't currently happen in CIR, but if we do get here, we need to
209 // handle the cd->isNoThrow() case.
210 if (const CapturedDecl *cd = dyn_cast_or_null<CapturedDecl>(d)) {
211 if (cd->isNothrow())
212 cgm.errorNYI(cd->getSourceRange(),
213 "emitStartEHSpec CapturedDecl nothrow");
214 }
215 return;
216 }
217
218 const FunctionProtoType *proto = fd->getType()->getAs<FunctionProtoType>();
219 if (!proto)
220 return;
221
223 // In C++17 and later, and in Wasm EH in any standard, 'throw()' aka
224 // EST_DynamicNone is treated the same way as noexcept. In earlier standards
225 // it is handled with 'throw(X...)'.
226 bool isDynamicSpec = est == EST_Dynamic ||
227 (est == EST_DynamicNone && !getLangOpts().CPlusPlus17 &&
228 !cgm.getCodeGenOpts().hasWasmExceptions());
229
230 // A specification that permits nothing to escape is a terminate scope: any
231 // exception that tries to leave the function calls std::terminate. Under
232 // -EHa a hardware exception can still occur, so there is no scope.
233 bool needsTerminate = !isDynamicSpec && proto->canThrow() == CT_Cannot &&
234 !getLangOpts().EHAsynch;
235
236 if (isDynamicSpec) {
237 // TODO: Revisit exception specifications for the MS ABI. There is a way
238 // to encode these in an object file but MSVC doesn't do anything with it.
239 if (getTarget().getCXXABI().isMicrosoft())
240 return;
241
242 // In Wasm EH, a specification with types is ignored with a warning for
243 // now. 'throw()' is a terminate scope, which the classification above
244 // takes care of.
245 // TODO Correctly handle exception specification in Wasm EH
246 if (cgm.getCodeGenOpts().hasWasmExceptions()) {
247 cgm.getDiags().Report(d->getLocation(),
248 diag::warn_wasm_dynamic_exception_spec_ignored)
250 return;
251 }
252
253 // Currently Emscripten EH only handles 'throw()' but not 'throw' with
254 // types. 'throw()' handling will be done in JS glue code so we don't need
255 // to do anything in that case. Just print a warning message in case of
256 // throw with types.
257 // TODO Correctly handle exception specification in Emscripten EH
258 if (getTarget().getCXXABI() == TargetCXXABI::WebAssembly &&
259 (cgm.getCodeGenOpts().getExceptionHandling() ==
261 cgm.getCodeGenOpts().getExceptionHandling() ==
263 est == EST_Dynamic)
264 cgm.getDiags().Report(d->getLocation(),
265 diag::warn_wasm_dynamic_exception_spec_ignored)
267 } else if (!needsTerminate) {
268 return;
269 }
270
271 mlir::Location loc = getLoc(fd->getSourceRange());
272
273 SmallVector<mlir::Attribute, 4> permittedTypes;
274 if (isDynamicSpec) {
275 for (QualType ty : proto->exceptions()) {
277 permittedTypes.push_back(
278 cgm.getAddrOfRTTIDescriptor(loc, exceptType, /*forEh=*/true));
279 }
280 }
281
282 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(curFn);
283 if (!funcOp.getPersonality())
284 funcOp.setPersonality(getPersonalityFn(cgm, EHPersonality::get(*this)));
285
286 bool emptyFilter = permittedTypes.empty();
287 ehSpecTryOp = cir::TryOp::create(
288 builder, loc,
289 // The try body holds the function body, which the caller emits after
290 // this function returns and emitEndEHSpec terminates.
291 /*tryBuilder=*/[](mlir::OpBuilder &, mlir::Location) {},
292 /*handlersBuilder=*/
293 [&](mlir::OpBuilder &b, mlir::Location loc,
294 mlir::OperationState &result) {
295 mlir::OpBuilder::InsertionGuard guard(b);
296 mlir::Type ehTokenTy = cir::EhTokenType::get(&getMLIRContext());
297
298 if (needsTerminate) {
299 mlir::Block *terminateBlock = b.createBlock(
300 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
301 cir::EhTerminateOp::create(b, loc, terminateBlock->getArgument(0));
302 return;
303 }
304
305 mlir::Block *filterBlock = b.createBlock(
306 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
307 if (emptyFilter)
308 cir::UnreachableOp::create(b, loc);
309 else
310 cir::ResumeOp::create(b, loc, filterBlock->getArgument(0));
311
312 mlir::Block *unexpectedBlock = b.createBlock(
313 result.addRegion(), /*insertPt=*/{}, {ehTokenTy}, {loc});
314 cir::EhUnexpectedOp::create(b, loc, unexpectedBlock->getArgument(0));
315 });
316
318 if (needsTerminate) {
319 // Any exception reaching the handler terminates the program, so it catches
320 // everything. The catch itself is performed by the runtime helper that
321 // cir.eh.terminate lowers to.
322 handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
323 } else {
324 handlerAttrs.push_back(cir::EhFilterAttr::get(
325 &getMLIRContext(), builder.getArrayAttr(permittedTypes)));
326 handlerAttrs.push_back(cir::EhUnexpectedAttr::get(&getMLIRContext()));
327 }
328 ehSpecTryOp.setHandlerTypesAttr(
329 mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
330
331 // Continue emitting into the try body.
332 builder.setInsertionPointToEnd(&ehSpecTryOp.getTryRegion().front());
333}
334
336 if (!ehSpecTryOp)
337 return;
338
339 cir::TryOp tryOp = ehSpecTryOp;
340 ehSpecTryOp = cir::TryOp();
341
342 // Terminate the try body. Emitting the function body may have left the last
343 // block without a terminator, for instance when control falls off the end.
344 mlir::Block *bodyExit = &tryOp.getTryRegion().back();
345 if (bodyExit->empty() ||
346 !bodyExit->back().hasTrait<mlir::OpTrait::IsTerminator>()) {
347 mlir::OpBuilder::InsertionGuard guard(builder);
348 builder.setInsertionPointToEnd(bodyExit);
349 builder.createYield(tryOp.getLoc());
350 }
351
352 // Continue emitting the function epilogue outside the try.
353 builder.setInsertionPointAfter(tryOp);
354}
355
357 const llvm::Triple &triple = getTarget().getTriple();
358 if (cgm.getLangOpts().OpenMPIsTargetDevice &&
359 (triple.isNVPTX() || triple.isAMDGCN())) {
360 cgm.errorNYI("emitCXXThrowExpr OpenMP with NVPTX or AMDGCN Triples");
361 return;
362 }
363
364 if (const Expr *subExpr = e->getSubExpr()) {
365 QualType throwType = subExpr->getType();
366 if (throwType->isObjCObjectPointerType()) {
367 cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
368 return;
369 }
370
371 cgm.getCXXABI().emitThrow(*this, e);
372 return;
373 }
374
375 cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
376}
377
379 // Make sure the exception object is cleaned up if there's an
380 // exception during initialization.
382
383 // __cxa_allocate_exception returns a void*; we need to cast this
384 // to the appropriate type for the object.
385 mlir::Type ty = convertTypeForMem(e->getType());
386 Address typedAddr = addr.withElementType(builder, ty);
387
388 // From LLVM's codegen:
389 // FIXME: this isn't quite right! If there's a final unelided call
390 // to a copy constructor, then according to [except.terminate]p1 we
391 // must call std::terminate() if that constructor throws, because
392 // technically that copy occurs after the exception expression is
393 // evaluated but before the exception is caught. But the best way
394 // to handle that is to teach EmitAggExpr to do the final copy
395 // differently if it can't be elided.
396 emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
397 /*isInitializer=*/true);
398
399 // Deactivate the cleanup block.
401}
402
404 const CXXCatchStmt *catchStmt, SmallVector<mlir::Attribute> &handlerAttrs) {
405 mlir::Location catchLoc = getLoc(catchStmt->getBeginLoc());
406
407 if (catchStmt->getExceptionDecl()) {
408 // FIXME: Dropping the reference type on the type into makes it
409 // impossible to correctly implement catch-by-reference
410 // semantics for pointers. Unfortunately, this is what all
411 // existing compilers do, and it's not clear that the standard
412 // personality routine is capable of doing this right. See C++ DR 388:
413 // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388
414 Qualifiers caughtTypeQuals;
415 QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType(
416 catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals);
417 if (caughtType->isObjCObjectPointerType()) {
418 cgm.errorNYI("addCatchHandlerAttr: caughtType ObjCObjectPointerType");
419 return;
420 }
421
422 CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType(
423 catchLoc, caughtType, catchStmt->getCaughtType());
424 handlerAttrs.push_back(typeInfo.rtti);
425 } else {
426 // No exception decl indicates '...', a catch-all.
427 handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
428 }
429}
430
431namespace {
432struct CallEndCatch final : EHScopeStack::Cleanup {
433 CallEndCatch(mlir::Value catchToken) : catchToken(catchToken) {}
434 mlir::Value catchToken;
435
436 void emit(CIRGenFunction &cgf, Flags flags) override {
437 cir::EndCatchOp::create(cgf.getBuilder(), cgf.getLoc(*cgf.currSrcLoc),
438 catchToken);
439 cir::YieldOp::create(cgf.getBuilder(), cgf.getLoc(*cgf.currSrcLoc));
440 }
441};
442} // namespace
443
444static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Value ehToken,
445 mlir::Type exnPtrTy) {
446 auto catchTokenTy = cir::CatchTokenType::get(cgf.getBuilder().getContext());
447 auto beginCatch = cir::BeginCatchOp::create(cgf.getBuilder(),
448 cgf.getBuilder().getUnknownLoc(),
449 catchTokenTy, exnPtrTy, ehToken);
450
451 cgf.ehStack.pushCleanup<CallEndCatch>(NormalAndEHCleanup,
452 beginCatch.getCatchToken());
453
454 return beginCatch.getExnPtr();
455}
456
457/// Get or create the catch-init copy thunk for \p catchParam.
458///
459/// The copy thunk has signature `void(T*, T*)` (where `T` is the catch
460/// parameter type) and contains the normal aggregate emission of the catch
461/// parameter's init expression.
462///
463/// The thunk name is keyed off the catch parameter's canonical type mangled
464/// name, so a single translation unit emits at most one thunk per catch type.
465static cir::FuncOp getOrCreateCopyThunk(CIRGenFunction &cgf,
466 const VarDecl &catchParam,
467 cir::PointerType paramAddrType,
468 SourceLocation clangLoc) {
469 mlir::Location loc = cgf.getLoc(clangLoc);
470 CIRGenModule &cgm = cgf.cgm;
471 CIRGenBuilderTy &builder = cgm.getBuilder();
472 mlir::ModuleOp mod = cgm.getModule();
473
474 const Expr *copyExpr = catchParam.getInit();
475 assert(copyExpr && "non-trivial copy expects a copy expression");
476
477 llvm::SmallString<128> thunkName;
478 llvm::raw_svector_ostream thunkNameStream(thunkName);
479 thunkNameStream << "__clang_cir_catch_copy_";
481 catchParam.getType(), thunkNameStream);
482
483 if (cir::FuncOp existing = cgm.lookupFuncOp(thunkName))
484 return existing;
485
486 mlir::Type voidTy = cir::VoidType::get(builder.getContext());
487 auto thunkTy = cir::FuncType::get({paramAddrType, paramAddrType}, voidTy,
488 /*isVarArg=*/false);
489
490 mlir::OpBuilder::InsertionGuard guard(builder);
491 builder.setInsertionPointToEnd(mod.getBody());
492 cir::FuncOp thunk = cir::FuncOp::create(builder, loc, thunkName, thunkTy);
493 cgm.insertGlobalSymbol(thunk);
494 thunk.setLinkage(cir::GlobalLinkageKind::LinkOnceODRLinkage);
495 thunk.setGlobalVisibility(cir::VisibilityKind::Hidden);
496 thunk->setAttr(cir::CIRDialect::getCatchCopyThunkAttrName(),
497 builder.getUnitAttr());
498
499 mlir::Block *entry = thunk.addEntryBlock();
500 builder.setInsertionPointToStart(entry);
501
502 // Use a fresh CIRGenFunction to drive the body emission. We need just enough
503 // state for emitAggExpr / emitCXXConstructorCall to compute the call-site
504 // argument attributes; the helper has no AST decl, no exception scopes, and
505 // no return value, so we bypass the full startFunction/finishFunction
506 // machinery.
507 CIRGenFunction subCgf(cgm, builder);
508 subCgf.curFn = thunk;
509
510 // Some emission paths (e.g. materializing temporaries for default args via
511 // emitAnyExprToTemp) need both a current source location and a lexical
512 // scope to anchor allocas. Since we bypass startFunction, install both
513 // explicitly for the lifetime of the thunk's body emission.
514 CIRGenFunction::SourceLocRAIIObject thunkLoc(subCgf, clangLoc);
515 CIRGenFunction::LexicalScope thunkScope(subCgf, loc, entry);
516
517 // Bind the OpaqueValueExpr at the source position of the catch parameter's
518 // copy expression to an LValue at the thunk's `src` block argument.
519 LValue srcLV = subCgf.makeNaturalAlignAddrLValue(entry->getArgument(1),
520 catchParam.getType());
522 subCgf, OpaqueValueExpr::findInCopyConstruct(copyExpr), srcLV);
523
524 // Drive the construction into the helper's `dest` block argument via the
525 // normal aggregate-emission machinery so that `ExprWithCleanups`,
526 // converting/inheriting constructors, and any future copy-construction
527 // shapes flow through unchanged.
528 Address destAddr = subCgf.makeNaturalAddressForPointer(
529 entry->getArgument(0), catchParam.getType(), clang::CharUnits::Zero());
530 subCgf.emitAggExpr(
531 copyExpr, AggValueSlot::forAddr(
534
535 cir::ReturnOp::create(builder, loc);
536 return thunk;
537}
538
539/// A "special initializer" callback for initializing a catch
540/// parameter during catch initialization.
542 mlir::Value ehToken, const VarDecl &catchParam,
543 SourceLocation loc) {
544 CanQualType catchType =
545 cgf.cgm.getASTContext().getCanonicalType(catchParam.getType());
546 cir::InitCatchKind kind;
547 bool shouldInitFromExnDirectly = false;
548
549 // If we're catching by reference, we can just cast the object
550 // pointer to the appropriate pointer.
551 if (isa<ReferenceType>(catchType)) {
552 QualType caughtType = cast<ReferenceType>(catchType)->getPointeeType();
553 if (const PointerType *ptr = dyn_cast<PointerType>(caughtType)) {
554 shouldInitFromExnDirectly = !ptr->getPointeeType()->isRecordType();
555 }
556 kind = cir::InitCatchKind::Reference;
557 } else {
558 cir::TypeEvaluationKind tek = cgf.getEvaluationKind(catchType);
559 if (tek == cir::TEK_Aggregate) {
560 assert(isa<RecordType>(catchType) && "unexpected catch type!");
561 const Expr *copyExpr = catchParam.getInit();
562 kind = !copyExpr ? cir::InitCatchKind::TrivialCopy
563 : cir::InitCatchKind::NonTrivialCopy;
564 } else {
565 // Scalars and complexes.
566 if (catchType->hasPointerRepresentation()) {
567 switch (catchType.getQualifiers().getObjCLifetime()) {
570 kind = cir::InitCatchKind::Objc;
571 break;
572
576 kind = cir::InitCatchKind::Pointer;
577 break;
578 }
579 } else {
580 kind = cir::InitCatchKind::Scalar;
581 }
582 }
583 }
584
586 Address paramAddr = var.getAllocatedAddress();
587 mlir::Location mloc = cgf.getLoc(loc);
588
589 if (kind == cir::InitCatchKind::NonTrivialCopy ||
590 (kind == cir::InitCatchKind::Reference && shouldInitFromExnDirectly)) {
591 // Sanitizer-checked construction (UBSan vptr/derived-class checks, etc.)
592 // would require additional adornments that cir.construct_catch_param does
593 // not yet carry.
595
596 mlir::FlatSymbolRefAttr copyFun{};
597 if (kind == cir::InitCatchKind::NonTrivialCopy) {
598 auto paramAddrType =
599 mlir::cast<cir::PointerType>(paramAddr.getPointer().getType());
600 cir::FuncOp thunk =
601 getOrCreateCopyThunk(cgf, catchParam, paramAddrType, loc);
602 copyFun = mlir::FlatSymbolRefAttr::get(thunk.getSymNameAttr());
603 }
604
605 cir::ConstructCatchParamOp::create(builder, mloc, ehToken,
606 paramAddr.getPointer(), kind, copyFun);
607 }
608
609 mlir::Value exnPtr = callBeginCatch(cgf, ehToken, builder.getVoidPtrTy());
610 cir::InitCatchParamOp::create(builder, mloc, exnPtr, paramAddr.getPointer(),
611 kind);
612 cgf.emitAutoVarCleanups(var);
613}
614
615/// Begins a catch statement by initializing the catch variable and
616/// calling __cxa_begin_catch.
618 mlir::Value ehToken) {
619 // We have to be very careful with the ordering of cleanups here:
620 // C++ [except.throw]p4:
621 // The destruction [of the exception temporary] occurs
622 // immediately after the destruction of the object declared in
623 // the exception-declaration in the handler.
624 //
625 // So the precise ordering is:
626 // 1. Construct catch variable.
627 // 2. begin_catch
628 // 3. Enter CallEndCatch cleanup
629 // 4. Enter dtor cleanup
630 //
631 VarDecl *catchParam = catchStmt->getExceptionDecl();
632 if (!catchParam) {
633 callBeginCatch(*this, ehToken, builder.getVoidPtrTy());
634 return;
635 }
636
637 // Emit the local. Make sure the alloca's superseed the current scope, since
638 // these are going to be consumed by `cir.catch`, which is not within the
639 // current scope.
640 initCatchParam(*this, builder, ehToken, *catchParam,
641 catchStmt->getBeginLoc());
642}
643
644mlir::LogicalResult
646 cxxTryBodyEmitter &bodyCallback) {
647 mlir::Location loc = getLoc(s.getSourceRange());
648
649 // Create a scope to hold try local storage for catch params.
650 mlir::OpBuilder::InsertPoint scopeIP;
651 cir::ScopeOp::create(
652 builder, loc,
653 /*scopeBuilder=*/[&](mlir::OpBuilder &b, mlir::Location loc) {
654 scopeIP = builder.saveInsertionPoint();
655 });
656
657 // Set personality function if not already set
658 auto funcOp = mlir::cast<cir::FuncOp>(curFn);
659 if (!funcOp.getPersonality())
660 funcOp.setPersonality(getPersonalityFn(cgm, EHPersonality::get(*this)));
661
662 mlir::OpBuilder::InsertionGuard guard(builder);
663 builder.restoreInsertionPoint(scopeIP);
664
665 const llvm::Triple &t = getTarget().getTriple();
666 // If we encounter a try statement on in an OpenMP target region offloaded
667 // to a GPU, we treat it as a basic block.
668 const bool isTargetDevice =
669 (cgm.getLangOpts().OpenMPIsTargetDevice && (t.isNVPTX() || t.isAMDGCN()));
670 if (isTargetDevice) {
671 cgm.errorNYI("emitCXXTryStmt: OpenMP target region offloaded to GPU");
672 return mlir::success();
673 }
674
675 mlir::Location tryLoc = getLoc(s.getBeginLoc());
676 SmallVector<mlir::Attribute> handlerAttrs;
677
678 CIRGenFunction::LexicalScope tryBodyScope{*this, tryLoc,
679 builder.getInsertionBlock()};
680
681 if (getLangOpts().EHAsynch) {
682 cgm.errorNYI("enterCXXTryStmt: EHAsynch");
683 return mlir::failure();
684 }
685
686 // Create the try operation.
687 mlir::LogicalResult tryRes = mlir::success();
688 auto tryOp = cir::TryOp::create(
689 builder, tryLoc,
690 /*tryBuilder=*/
691 [&](mlir::OpBuilder &b, mlir::Location loc) {
692 // Create a RunCleanupsScope that allows us to apply any cleanups that
693 // are created for statements within the try body before exiting the
694 // try body.
695 RunCleanupsScope tryBodyCleanups(*this);
696 if (bodyCallback(*this).failed())
697 tryRes = mlir::failure();
698 tryBodyCleanups.forceCleanup();
699 if (!builder.getBlock()->mightHaveTerminator() ||
700 !builder.getBlock()->getTerminator())
701 cir::YieldOp::create(builder, loc);
702 },
703 /*handlersBuilder=*/
704 [&](mlir::OpBuilder &b, mlir::Location loc,
705 mlir::OperationState &result) {
706 mlir::OpBuilder::InsertionGuard guard(b);
707 bool hasCatchAll = false;
708 unsigned numHandlers = s.getNumHandlers();
709 mlir::Type ehTokenTy = cir::EhTokenType::get(&getMLIRContext());
710 for (unsigned i = 0; i != numHandlers; ++i) {
711 const CXXCatchStmt *catchStmt = s.getHandler(i);
712 if (!catchStmt->getExceptionDecl())
713 hasCatchAll = true;
714 mlir::Region *region = result.addRegion();
715 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
716 addCatchHandlerAttr(catchStmt, handlerAttrs);
717 }
718 if (!hasCatchAll) {
719 // Create unwind region.
720 mlir::Region *region = result.addRegion();
721 mlir::Block *unwindBlock =
722 builder.createBlock(region, /*insertPt=*/{}, {ehTokenTy}, {loc});
723 cir::ResumeOp::create(builder, loc, unwindBlock->getArgument(0));
724 handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
725 }
726 });
727
728 if (tryRes.failed())
729 return mlir::failure();
730
731 // Add final array of clauses into TryOp.
732 tryOp.setHandlerTypesAttr(
733 mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs));
734
735 // Emit the catch handler bodies. This has to be done after the try op is
736 // created and in place so that we can find the insertion point for the
737 // catch parameter alloca.
738 unsigned numHandlers = s.getNumHandlers();
739 for (unsigned i = 0; i != numHandlers; ++i) {
740 const CXXCatchStmt *catchStmt = s.getHandler(i);
741 mlir::Region *handler = &tryOp.getHandlerRegions()[i];
742 mlir::Location handlerLoc = getLoc(catchStmt->getCatchLoc());
743
744 mlir::OpBuilder::InsertionGuard guard(builder);
745 builder.setInsertionPointToStart(&handler->front());
746
747 // Get the !cir.eh_token block argument from the handler region.
748 mlir::Value ehToken = handler->front().getArgument(0);
749
750 // Enter a cleanup scope, including the catch variable and the
751 // end-catch.
752 RunCleanupsScope handlerScope(*this);
753
754 // Initialize the catch variable.
755 // TODO(cir): Move this out of CXXABI.
757 emitBeginCatch(catchStmt, ehToken);
758
759 // Emit the PGO counter increment.
761
762 // Perform the body of the catch.
763 [[maybe_unused]] mlir::LogicalResult emitResult =
764 emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
765 assert(emitResult.succeeded() && "failed to emit catch handler block");
766
767 // [except.handle]p11:
768 // The currently handled exception is rethrown if control
769 // reaches the end of a handler of the function-try-block of a
770 // constructor or destructor.
771
772 // TODO(cir): Handle implicit rethrow?
773
774 // Fall out through the catch cleanups.
775 handlerScope.forceCleanup();
776
777 mlir::Block *block = &handler->getBlocks().back();
778 if (block->empty() ||
779 !block->back().hasTrait<mlir::OpTrait::IsTerminator>()) {
780 mlir::OpBuilder::InsertionGuard guard(builder);
781 builder.setInsertionPointToEnd(block);
782 builder.createYield(handlerLoc);
783 }
784 }
785
786 return mlir::success();
787}
788
789mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
790 if (s.getTryBlock()->body_empty())
791 return mlir::LogicalResult::success();
792
793 struct simpleTryBodyEmitter final : cxxTryBodyEmitter {
794 const clang::CXXTryStmt &s;
795 simpleTryBodyEmitter(const clang::CXXTryStmt &s) : s(s) {}
796
797 mlir::LogicalResult operator()(CIRGenFunction &cgf) override {
798 return cgf.emitStmt(s.getTryBlock(), /*useCurrentScope=*/true);
799 }
800 ~simpleTryBodyEmitter() override = default;
801 };
802
803 simpleTryBodyEmitter emitter{s};
804
805 return emitCXXTryStmt(s, emitter);
806}
807
808// in classic codegen this function is mapping to `isInvokeDest` previously
809// and currently it's mapping to the conditions that performs early returns in
810// `getInvokeDestImpl`, in CIR we need the condition to know if the EH scope
811// may throw exception or now.
813 // If exceptions are disabled/ignored and SEH is not in use, then there is
814 // no invoke destination. SEH "works" even if exceptions are off. In
815 // practice, this means that C++ destructors and other EH cleanups don't
816 // run, which is consistent with MSVC's behavior, except in the presence of
817 // -EHa
818 const LangOptions &lo = cgm.getLangOpts();
819 if (!lo.Exceptions || lo.IgnoreExceptions) {
820 if (!lo.Borland && !lo.MicrosoftExt)
821 return false;
822 cgm.errorNYI("isInvokeDest: no exceptions or ignore exception");
823 return false;
824 }
825
826 // CUDA device code doesn't have exceptions.
827 if (lo.CUDA && lo.CUDAIsDevice)
828 return false;
829
830 return ehStack.requiresCatchOrCleanup();
831}
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 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 mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Value ehToken, mlir::Type exnPtrTy)
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 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.
static const EHPersonality & getSEHPersonalityMSVC(const llvm::Triple &triple)
*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.
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)
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:1212
const Expr * getSubExpr() const
Definition ExprCXX.h:1232
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:5082
static CharUnits Zero()
Zero - Construct a CharUnits quantity of zero.
Definition CharUnits.h:53
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
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
bool usesSEHTry() const
Indicates the function uses __try.
Definition Decl.h:2645
SourceRange getExceptionSpecSourceRange() const
Attempt to compute an informative source range covering the function exception specification,...
Definition Decl.cpp:4095
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:4608
Represents a prototype with parameter type info, e.g.
Definition TypeBase.h:5398
ExceptionSpecificationType getExceptionSpecType() const
Get the kind of exception specification on this function.
Definition TypeBase.h:5705
CanThrowResult canThrow() const
Determine whether this function type has a non-throwing exception specification.
Definition Type.cpp:4098
ArrayRef< QualType > exceptions() const
Definition TypeBase.h:5852
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...
clang::ObjCRuntime ObjCRuntime
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.
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
static const OpaqueValueExpr * findInCopyConstruct(const Expr *expr)
Given an expression which invokes a copy constructor — i.e.
Definition Expr.cpp:5207
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3396
A (possibly-)qualified type.
Definition TypeBase.h:938
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8468
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:8613
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8522
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
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:8844
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9264
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
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
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.
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