clang 24.0.0git
EHABILowering.cpp
Go to the documentation of this file.
1//===- EHABILowering.cpp - Lower flattened CIR EH ops to ABI-specific form ===//
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 file implements a pass that lowers ABI-agnostic flattened CIR exception
10// handling operations into an ABI-specific form. Currently only the Itanium
11// C++ ABI is supported.
12//
13// The Itanium ABI lowering performs these transformations:
14// - cir.eh.initiate → cir.eh.inflight_exception (landing pad)
15// - cir.eh.dispatch → cir.eh.typeid + cir.cmp + cir.brcond chains
16// - cir.begin_cleanup → (removed)
17// - cir.end_cleanup → (removed)
18// - cir.begin_catch → call to __cxa_begin_catch
19// - cir.end_catch → call to __cxa_end_catch
20// - cir.eh.terminate → call to __clang_call_terminate + unreachable
21// - cir.eh.unexpected → call to __cxa_call_unexpected + unreachable
22// - cir.resume → cir.resume.flat
23// - !cir.eh_token values → (!cir.ptr<!void>, !u32i) value pairs
24// - cir.construct_catch_param → __cxa_get_exception_ptr + inlined
25// catch-copy thunk body
26// - personality function set on functions requiring EH
27//
28//===----------------------------------------------------------------------===//
29
30#include "PassDetail.h"
31#include "mlir/IR/Builders.h"
32#include "mlir/IR/IRMapping.h"
33#include "mlir/IR/PatternMatch.h"
40#include "llvm/ADT/DenseMap.h"
41#include "llvm/ADT/SetVector.h"
42#include "llvm/ADT/SmallVector.h"
43#include "llvm/TargetParser/Triple.h"
44
45using namespace mlir;
46using namespace cir;
47
48namespace mlir {
49#define GEN_PASS_DEF_CIREHABILOWERING
50#include "clang/CIR/Dialect/Passes.h.inc"
51} // namespace mlir
52
53namespace {
54
55//===----------------------------------------------------------------------===//
56// Shared utilities
57//===----------------------------------------------------------------------===//
58
59/// Ensure a function with the given name and type exists in the module. If it
60/// does not exist, create a private external declaration.
61static cir::FuncOp getOrCreateRuntimeFuncDecl(mlir::ModuleOp mod,
62 mlir::Location loc,
63 StringRef name,
64 cir::FuncType funcTy) {
65 if (auto existing = mod.lookupSymbol<cir::FuncOp>(name))
66 return existing;
67
68 mlir::OpBuilder builder(mod.getContext());
69 builder.setInsertionPointToEnd(mod.getBody());
70 auto funcOp = cir::FuncOp::create(builder, loc, name, funcTy);
71 funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
72 funcOp.setPrivate();
73 return funcOp;
74}
75
76//===----------------------------------------------------------------------===//
77// EH ABI Lowering Base Class
78//===----------------------------------------------------------------------===//
79
80/// Abstract base class for exception-handling ABI lowering.
81/// Each supported ABI (Itanium, Microsoft, etc.) provides a concrete subclass.
82class EHABILowering {
83public:
84 explicit EHABILowering(mlir::ModuleOp mod)
85 : mod(mod), ctx(mod.getContext()), builder(ctx) {}
86 virtual ~EHABILowering() = default;
87
88 /// Lower all EH operations in the module to an ABI-specific form.
89 virtual mlir::LogicalResult run() = 0;
90
91protected:
92 mlir::ModuleOp mod;
93 mlir::MLIRContext *ctx;
94 mlir::OpBuilder builder;
95};
96
97//===----------------------------------------------------------------------===//
98// Itanium EH ABI Lowering
99//===----------------------------------------------------------------------===//
100
101/// Lowers flattened CIR EH operations to the Itanium C++ ABI form.
102///
103/// The entry point is run(), which iterates over all functions and
104/// calls lowerFunc() for each. lowerFunc() drives all lowering from
105/// cir.eh.initiate operations: every other EH op (begin/end_cleanup,
106/// eh.dispatch, begin/end_catch, resume) is reachable by tracing the
107/// eh_token produced by the initiate through its users.
108class ItaniumEHLowering : public EHABILowering {
109public:
110 using EHABILowering::EHABILowering;
111 mlir::LogicalResult run() override;
112
113private:
114 /// Maps a !cir.eh_token value to its Itanium ABI replacement pair:
115 /// an exception pointer (!cir.ptr<!void>) and a type id (!u32i).
116 using EhTokenMap = DenseMap<mlir::Value, std::pair<mlir::Value, mlir::Value>>;
117
118 cir::VoidType voidType;
119 cir::PointerType voidPtrType;
120 cir::PointerType u8PtrType;
121 cir::IntType u32Type;
122 cir::IntType s32Type;
123
124 // Cached runtime function declarations, initialized when needed by
125 // ensureRuntimeDecls().
126 cir::FuncOp personalityFunc;
127 cir::FuncOp beginCatchFunc;
128 cir::FuncOp endCatchFunc;
129 cir::FuncOp getExceptionPtrFunc;
130 cir::FuncOp clangCallTerminateFunc;
131 cir::FuncOp cxaThrowFunc;
132 cir::FuncOp cxaRethrowFunc;
133 cir::FuncOp cxaCallUnexpectedFunc;
134
135 DenseMap<mlir::StringAttr, cir::FuncOp> catchCopyThunks;
136
137 constexpr const static ::llvm::StringLiteral kGxxPersonality =
138 "__gxx_personality_v0";
139
140 void ensureRuntimeDecls(mlir::Location loc);
141 void ensureClangCallTerminate(mlir::Location loc);
142 void ensureCxaThrowDecl(mlir::Location loc);
143 void ensureCxaRethrowDecl(mlir::Location loc);
144 void ensureCxaCallUnexpectedDecl(mlir::Location loc);
145 mlir::Block *buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc);
146 mlir::FailureOr<cir::FuncOp>
147 resolveCatchCopyThunk(cir::ConstructCatchParamOp op);
148 mlir::LogicalResult lowerFunc(cir::FuncOp funcOp);
149 mlir::LogicalResult
150 lowerEhInitiate(cir::EhInitiateOp initiateOp,
151 llvm::ArrayRef<cir::EhDispatchOp> reachedDispatches,
152 bool reachesCleanup, EhTokenMap &ehTokenMap);
153 void lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value exnPtr,
154 mlir::Value typeId);
155 mlir::LogicalResult lowerConstructCatchParam(cir::ConstructCatchParamOp op,
156 mlir::Value exnPtr);
157 void lowerInitCatchParam(cir::InitCatchParamOp op);
158 mlir::LogicalResult lowerTryThrow(cir::TryThrowOp op);
159};
160
161/// Lower all EH operations in the module to the Itanium-specific form.
162mlir::LogicalResult ItaniumEHLowering::run() {
163 // Pre-compute the common types used throughout all function lowerings.
164 // TODO(cir): Move these to the base class if they are also needed for MSVC.
165 voidType = cir::VoidType::get(ctx);
166 voidPtrType = cir::PointerType::get(voidType);
167 auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false);
168 u8PtrType = cir::PointerType::get(u8Type);
169 u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false);
170 s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true);
171
172 for (cir::FuncOp funcOp : mod.getOps<cir::FuncOp>()) {
173 if (mlir::failed(lowerFunc(funcOp)))
174 return mlir::failure();
175 }
176 return mlir::success();
177}
178
179/// Ensure the necessary Itanium runtime function declarations exist in the
180/// module.
181void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) {
182 // TODO(cir): Handle other personality functions. This probably isn't needed
183 // here if we fix codegen to always set the personality function.
184 if (!personalityFunc) {
185 auto personalityFuncTy = cir::FuncType::get({}, s32Type, /*isVarArg=*/true);
186 personalityFunc = getOrCreateRuntimeFuncDecl(mod, loc, kGxxPersonality,
187 personalityFuncTy);
188 }
189
190 if (!beginCatchFunc) {
191 auto beginCatchFuncTy =
192 cir::FuncType::get({voidPtrType}, u8PtrType, /*isVarArg=*/false);
193 beginCatchFunc = getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_begin_catch",
194 beginCatchFuncTy);
195 }
196
197 if (!endCatchFunc) {
198 auto endCatchFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
199 endCatchFunc =
200 getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_end_catch", endCatchFuncTy);
201 }
202
203 if (!getExceptionPtrFunc) {
204 auto getExceptionPtrFuncTy =
205 cir::FuncType::get({voidPtrType}, u8PtrType, /*isVarArg=*/false);
206 getExceptionPtrFunc = getOrCreateRuntimeFuncDecl(
207 mod, loc, "__cxa_get_exception_ptr", getExceptionPtrFuncTy);
208 }
209}
210
211/// Ensure the __clang_call_terminate function exists in the module. This
212/// function is defined with a body that calls __cxa_begin_catch followed by
213/// std::terminate, matching the behavior of Clang's LLVM IR codegen.
214///
215/// void __clang_call_terminate(void *exn) nounwind noreturn {
216/// __cxa_begin_catch(exn);
217/// std::terminate();
218/// unreachable;
219/// }
220void ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) {
221 if (clangCallTerminateFunc)
222 return;
223
224 ensureRuntimeDecls(loc);
225
226 if (auto existing = mod.lookupSymbol<cir::FuncOp>("__clang_call_terminate")) {
227 clangCallTerminateFunc = existing;
228 return;
229 }
230
231 auto funcTy = cir::FuncType::get({voidPtrType}, voidType, /*isVarArg=*/false);
232 builder.setInsertionPointToEnd(mod.getBody());
233 auto funcOp =
234 cir::FuncOp::create(builder, loc, "__clang_call_terminate", funcTy);
235 funcOp.setLinkage(cir::GlobalLinkageKind::LinkOnceODRLinkage);
236 funcOp.setGlobalVisibility(cir::VisibilityKind::Hidden);
237
238 mlir::Block *entryBlock = funcOp.addEntryBlock();
239 builder.setInsertionPointToStart(entryBlock);
240 mlir::Value exnArg = entryBlock->getArgument(0);
241
242 auto catchCall = cir::CallOp::create(
243 builder, loc, mlir::FlatSymbolRefAttr::get(beginCatchFunc), u8PtrType,
244 mlir::ValueRange{exnArg});
245 catchCall.setNothrowAttr(builder.getUnitAttr());
246
247 auto terminateFuncDecl = getOrCreateRuntimeFuncDecl(
248 mod, loc, "_ZSt9terminatev",
249 cir::FuncType::get({}, voidType, /*isVarArg=*/false));
250 terminateFuncDecl->setAttr(cir::CIRDialect::getNoReturnAttrName(),
251 builder.getUnitAttr());
252 auto terminateCall = cir::CallOp::create(
253 builder, loc, mlir::FlatSymbolRefAttr::get(terminateFuncDecl), voidType,
254 mlir::ValueRange{});
255 terminateCall.setNothrowAttr(builder.getUnitAttr());
256 terminateCall->setAttr(cir::CIRDialect::getNoReturnAttrName(),
257 builder.getUnitAttr());
258
259 cir::UnreachableOp::create(builder, loc);
260
261 funcOp->setAttr(cir::CIRDialect::getNoReturnAttrName(),
262 builder.getUnitAttr());
263 clangCallTerminateFunc = funcOp;
264}
265
266/// Ensure the __cxa_throw runtime function is declared in the module.
267///
268/// void __cxa_throw(void *exception, void *type_info, void *dtor);
269void ItaniumEHLowering::ensureCxaThrowDecl(mlir::Location loc) {
270 if (cxaThrowFunc)
271 return;
272 auto throwFuncTy = cir::FuncType::get({voidPtrType, voidPtrType, voidPtrType},
273 voidType, /*isVarArg=*/false);
274 cxaThrowFunc =
275 getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_throw", throwFuncTy);
276}
277
278/// Ensure the __cxa_rethrow runtime function is declared in the module.
279///
280/// void __cxa_rethrow();
281void ItaniumEHLowering::ensureCxaRethrowDecl(mlir::Location loc) {
282 if (cxaRethrowFunc)
283 return;
284 auto rethrowFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
285 cxaRethrowFunc =
286 getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_rethrow", rethrowFuncTy);
287}
288
289/// void __cxa_call_unexpected(void *exn);
290void ItaniumEHLowering::ensureCxaCallUnexpectedDecl(mlir::Location loc) {
291 if (cxaCallUnexpectedFunc)
292 return;
293 auto unexpectedFuncTy =
294 cir::FuncType::get({voidPtrType}, voidType, /*isVarArg=*/false);
295 cxaCallUnexpectedFunc = getOrCreateRuntimeFuncDecl(
296 mod, loc, "__cxa_call_unexpected", unexpectedFuncTy);
297}
298
299/// Create a terminate landing pad block at the end of the specified function.
300mlir::Block *ItaniumEHLowering::buildTerminateBlock(cir::FuncOp funcOp,
301 mlir::Location loc) {
302 assert(clangCallTerminateFunc &&
303 "ensureClangCallTerminate must run before buildTerminateBlock");
304 mlir::Region &body = funcOp.getRegion();
305 mlir::Block *terminateBlock = builder.createBlock(&body, body.end());
306 auto inflight = cir::EhInflightOp::create(
307 builder, loc, /*cleanup=*/false, /*catch_all=*/true,
308 /*catch_type_list=*/mlir::ArrayAttr{},
309 /*filter_type_list=*/mlir::ArrayAttr{});
310 auto terminateCall = cir::CallOp::create(
311 builder, loc, mlir::FlatSymbolRefAttr::get(clangCallTerminateFunc),
312 voidType, mlir::ValueRange{inflight.getExceptionPtr()});
313 terminateCall.setNothrowAttr(builder.getUnitAttr());
314 terminateCall->setAttr(cir::CIRDialect::getNoReturnAttrName(),
315 builder.getUnitAttr());
316 cir::UnreachableOp::create(builder, loc);
317 return terminateBlock;
318}
319
320/// Read-only walk of the eh_token graph from an initiate's root token,
321/// collecting every cir.eh.dispatch its exception can reach, innermost first.
322/// The token flows through cleanups to the innermost dispatch and then, via
323/// that dispatch's continue-unwind edge, on to each enclosing dispatch (nested
324/// try/catch). The walk follows the token into catch-handler blocks too, but
325/// it dead-ends there because cir.begin_catch consumes the eh_token (producing
326/// a catch_token), so only the unwind chain yields further dispatches.
327///
328/// This is computed before any destructive lowering so a landing pad's catch
329/// types -- which are a property of the EH graph -- do not depend on the order
330/// in which the destructive per-initiate traversal tears down shared
331/// token-graph edges. \p dispatches is left empty for a cleanup-only initiate
332/// that reaches no dispatch (e.g. a path that only resumes).
333static void collectReachableDispatches(
334 mlir::Value rootToken,
335 llvm::SmallSetVector<cir::EhDispatchOp, 4> &dispatches,
336 bool &reachesCleanup) {
337 llvm::SmallVector<mlir::Value> worklist;
338 llvm::SmallPtrSet<mlir::Value, 8> visited;
339 worklist.push_back(rootToken);
340 // Breadth-first (process in insertion order) so dispatches are discovered
341 // innermost first, matching the order catch clauses must appear in the
342 // landing pad.
343 for (unsigned i = 0; i < worklist.size(); ++i) {
344 mlir::Value current = worklist[i];
345 if (!visited.insert(current).second)
346 continue;
347 for (mlir::OpOperand &use : current.getUses()) {
348 mlir::Operation *user = use.getOwner();
349 // A cleanup anywhere on the unwind path (this initiate's own cleanup or
350 // an enclosing scope's) means the landing pad must carry the cleanup
351 // clause so destructors still run when a foreign exception unwinds
352 // through this frame.
353 if (mlir::isa<cir::BeginCleanupOp>(user))
354 reachesCleanup = true;
355 if (auto dispatch = mlir::dyn_cast<cir::EhDispatchOp>(user))
356 dispatches.insert(dispatch);
357 // Follow the token into eh_token block arguments of successor blocks.
358 for (unsigned s = 0, e = user->getNumSuccessors(); s < e; ++s)
359 for (mlir::BlockArgument arg : user->getSuccessor(s)->getArguments())
360 if (mlir::isa<cir::EhTokenType>(arg.getType()))
361 worklist.push_back(arg);
362 }
363 }
364}
365
366/// Lower all EH operations in a single function.
367mlir::LogicalResult ItaniumEHLowering::lowerFunc(cir::FuncOp funcOp) {
368 if (funcOp.isDeclaration())
369 return mlir::success();
370
371 // All EH lowering follows from cir.eh.initiate operations. The token each
372 // initiate produces connects it to every other EH op in the function
373 // (begin/end_cleanup, eh.dispatch, begin/end_catch, resume) through the
374 // token graph. A single walk to collect initiates is therefore sufficient.
375 SmallVector<cir::EhInitiateOp> initiateOps;
376 funcOp.walk([&](cir::EhInitiateOp op) { initiateOps.push_back(op); });
377 if (initiateOps.empty())
378 return mlir::success();
379
380 ensureRuntimeDecls(funcOp.getLoc());
381
382 // Set the personality function if it is not already set.
383 // TODO(cir): The personality function should already have been set by this
384 // point. If we've seen a try operation, it will have been set by
385 // emitCXXTryStmt. If we only have cleanups, it may not have been set. We
386 // need to fix that in CodeGen. This is a placeholder until that is done.
387 if (!funcOp.getPersonality())
388 funcOp.setPersonality(kGxxPersonality);
389
390 // Compute, read-only and before any destructive lowering, the dispatches each
391 // initiate's exception can reach (innermost first; more than one for nested
392 // try/catch). A landing pad's catch types are a property of the EH graph, so
393 // deriving them here keeps them independent of the order in which the
394 // destructive per-initiate traversal in lowerEhInitiate tears down shared
395 // token-graph edges. Otherwise a sibling or outer dispatch could be missed,
396 // leaving a landing pad without its catch clause (it would resume past the
397 // handler to std::terminate) or an un-lowered leftover dispatch.
398 // Per initiate: the dispatches its exception can reach (innermost first) and
399 // whether a cleanup lies on its unwind path. Both are properties of the EH
400 // graph and are always consumed together for the same initiate, so they live
401 // in one map keyed by the initiate op.
402 struct InitiateEHInfo {
403 llvm::SmallSetVector<cir::EhDispatchOp, 4> reachedDispatches;
404 bool reachesCleanup = false;
405 };
406 llvm::DenseMap<mlir::Operation *, InitiateEHInfo> initiateInfo;
407 llvm::SmallSetVector<cir::EhDispatchOp, 4> dispatchesToLower;
408 for (cir::EhInitiateOp initiateOp : initiateOps) {
409 InitiateEHInfo &info = initiateInfo[initiateOp.getOperation()];
410 collectReachableDispatches(initiateOp.getEhToken(), info.reachedDispatches,
411 info.reachesCleanup);
412 dispatchesToLower.insert(info.reachedDispatches.begin(),
413 info.reachedDispatches.end());
414 }
415
416 EhTokenMap ehTokenMap;
417 for (cir::EhInitiateOp initiateOp : initiateOps) {
418 const InitiateEHInfo &info = initiateInfo[initiateOp.getOperation()];
419 if (mlir::failed(lowerEhInitiate(initiateOp,
420 info.reachedDispatches.getArrayRef(),
421 info.reachesCleanup, ehTokenMap)))
422 return mlir::failure();
423 }
424
425 // Lower each dispatch exactly once. Every initiate's token block-argument
426 // (ptr, u32) replacements are registered in ehTokenMap by now, so each
427 // dispatch's own (exnPtr, typeId) pair is available. lowerDispatch erases
428 // the dispatch after building its comparison chain.
429 for (cir::EhDispatchOp dispatch : dispatchesToLower) {
430 auto [exnPtr, typeId] = ehTokenMap.lookup(dispatch.getEhToken());
431 assert(exnPtr && typeId &&
432 "dispatch eh_token must be registered in ehTokenMap");
433 lowerDispatch(dispatch, exnPtr, typeId);
434 }
435
436 // Remove the !cir.eh_token block arguments that were replaced by (ptr, u32)
437 // pairs. Iterate in reverse to preserve argument indices during removal.
438 for (mlir::Block &block : funcOp.getBody()) {
439 for (int i = block.getNumArguments() - 1; i >= 0; --i) {
440 if (mlir::isa<cir::EhTokenType>(block.getArgument(i).getType()))
441 block.eraseArgument(i);
442 }
443 }
444
445 // Lower any cir.init_catch_param ops in this function. These materialize
446 // the catch parameter local from the (already lowered) begin_catch result,
447 // and are independent of the eh_token graph traversal above.
448 SmallVector<cir::InitCatchParamOp> initCatchOps;
449 funcOp.walk([&](cir::InitCatchParamOp op) { initCatchOps.push_back(op); });
450 for (cir::InitCatchParamOp op : initCatchOps)
451 lowerInitCatchParam(op);
452
453 // Lower any cir.try_throw ops in this function to cir.try_call of
454 // __cxa_throw / __cxa_rethrow. These are produced by FlattenCFG when a
455 // cir.throw appears inside a cleanup scope or try region.
456 SmallVector<cir::TryThrowOp> tryThrowOps;
457 funcOp.walk([&](cir::TryThrowOp op) { tryThrowOps.push_back(op); });
458 for (cir::TryThrowOp op : tryThrowOps)
459 if (mlir::failed(lowerTryThrow(op)))
460 return mlir::failure();
461
462 return mlir::success();
463}
464
465/// Lower all EH operations connected to a single cir.eh.initiate.
466///
467/// The cir.eh.initiate is the root of a token graph. The token it produces
468/// flows through branch edges to consuming operations:
469///
470/// cir.eh.initiate → (via cir.br) → cir.begin_cleanup
471/// → cir.end_cleanup (via cleanup_token)
472/// → (via cir.br) → cir.eh.dispatch
473/// → (successors) →
474/// cir.begin_catch
475/// → cir.end_catch
476/// (via catch_token)
477/// → cir.resume
478///
479/// A single traversal of the token graph discovers and processes every
480/// connected op inline. The inflight_exception is created up-front without
481/// a catch_type_list; when the dispatch is encountered during traversal,
482/// the catch types are read and set on the inflight op.
483///
484/// Dispatch ops are not lowered here; they are lowered once by the caller after
485/// every initiate has been processed (a dispatch can be shared by sibling
486/// initiates), so this traversal only registers the catch-handler block
487/// arguments reachable through them.
488///
489/// \p ehTokenMap is shared across all initiates in the function so that block
490/// arguments reachable from multiple sibling initiates are registered once.
491mlir::LogicalResult ItaniumEHLowering::lowerEhInitiate(
492 cir::EhInitiateOp initiateOp,
493 llvm::ArrayRef<cir::EhDispatchOp> reachedDispatches, bool reachesCleanup,
494 EhTokenMap &ehTokenMap) {
495 mlir::Value rootToken = initiateOp.getEhToken();
496
497 // The catch clauses for this landing pad come from the dispatches its
498 // exception reaches (computed read-only by the caller before any destructive
499 // lowering). For nested try/catch the exception can reach several dispatches
500 // innermost first, so the landing pad lists their catch types in that order
501 // (matching classic CodeGen), stopping at a catch-all since nothing escapes
502 // it. Deriving this from the original EH graph -- rather than during the
503 // destructive token-graph traversal below -- keeps it correct regardless of
504 // the order in which sibling/nested initiates are lowered.
505 mlir::ArrayAttr catchTypeList;
506 mlir::ArrayAttr filterTypeList;
507 bool catchAll = false;
508 SmallVector<mlir::Attribute> typeSymbols;
509 for (cir::EhDispatchOp dispatch : reachedDispatches) {
510 if (mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr()) {
511 for (mlir::Attribute attr : catchTypes) {
512 if (auto filter = mlir::dyn_cast<cir::EhFilterAttr>(attr)) {
513 SmallVector<mlir::Attribute> filterSymbols;
514 // A filter terminates the landing-pad clause list the same way a
515 // catch-all does. Nothing outside the specification is reachable.
516 for (mlir::Attribute typeAttr : filter.getPermittedTypes()) {
517 auto globalView = mlir::cast<cir::GlobalViewAttr>(typeAttr);
518 filterSymbols.push_back(globalView.getSymbol());
519 }
520 filterTypeList = builder.getArrayAttr(filterSymbols);
521 continue;
522 }
523 typeSymbols.push_back(
524 mlir::cast<cir::GlobalViewAttr>(attr).getSymbol());
525 }
526 }
527 if (dispatch.getDefaultIsCatchAll() || filterTypeList) {
528 catchAll = dispatch.getDefaultIsCatchAll();
529 // A catch-all or filter handles the remaining exceptions, so it
530 // stops the unwind. No enclosing dispatch is reachable past it.
531 assert(dispatch == reachedDispatches.back() &&
532 "catch-all or filter must be the last reachable dispatch");
533 break;
534 }
535 }
536 if (!typeSymbols.empty())
537 catchTypeList = builder.getArrayAttr(typeSymbols);
538
539 builder.setInsertionPoint(initiateOp);
540 auto inflightOp = cir::EhInflightOp::create(
541 builder, initiateOp.getLoc(), initiateOp.getCleanup() || reachesCleanup,
542 catchAll, catchTypeList, filterTypeList);
543
544 ehTokenMap[rootToken] = {inflightOp.getExceptionPtr(),
545 inflightOp.getTypeId()};
546
547 // Single traversal of the token graph. For each token value (the root token
548 // or a block argument that carries it), we snapshot its users, register
549 // (ptr, u32) replacement arguments on successor blocks, then process every
550 // user inline. This avoids collecting ops into separate vectors.
551 SmallVector<mlir::Value> worklist;
552 SmallPtrSet<mlir::Value, 8> visited;
553 worklist.push_back(rootToken);
554
555 while (!worklist.empty()) {
556 mlir::Value current = worklist.pop_back_val();
557 if (!visited.insert(current).second)
558 continue;
559
560 // Snapshot users before modifying any of them (erasing ops during
561 // iteration would invalidate the use-list iterator).
562 SmallVector<mlir::Operation *> users;
563 for (mlir::OpOperand &use : current.getUses())
564 users.push_back(use.getOwner());
565
566 // Register replacement block arguments on successor blocks (extending the
567 // worklist), then lower the op itself.
568 for (mlir::Operation *user : users) {
569 // Trace into successor blocks to register (ptr, u32) replacement
570 // arguments for any !cir.eh_token block arguments found there. Even
571 // if a block arg was already registered by a sibling initiate, it is
572 // still added to the worklist so that the traversal can reach the
573 // shared dispatch to read catch types.
574 for (unsigned s = 0; s < user->getNumSuccessors(); ++s) {
575 mlir::Block *succ = user->getSuccessor(s);
576 for (mlir::BlockArgument arg : succ->getArguments()) {
577 if (!mlir::isa<cir::EhTokenType>(arg.getType()))
578 continue;
579 if (!ehTokenMap.count(arg)) {
580 mlir::Value ptrArg = succ->addArgument(voidPtrType, arg.getLoc());
581 mlir::Value u32Arg = succ->addArgument(u32Type, arg.getLoc());
582 ehTokenMap[arg] = {ptrArg, u32Arg};
583 }
584 worklist.push_back(arg);
585 }
586 }
587
588 if (auto op = mlir::dyn_cast<cir::BeginCleanupOp>(user)) {
589 // begin_cleanup / end_cleanup are no-ops for Itanium. Erase the
590 // end_cleanup first (drops the cleanup_token use) then the begin.
591 for (auto &tokenUsers :
592 llvm::make_early_inc_range(op.getCleanupToken().getUses())) {
593 if (auto endOp =
594 mlir::dyn_cast<cir::EndCleanupOp>(tokenUsers.getOwner()))
595 endOp.erase();
596 }
597 op.erase();
598 } else if (auto op = mlir::dyn_cast<cir::BeginCatchOp>(user)) {
599 // Replace end_catch → __cxa_end_catch (drops the catch_token use),
600 // then replace begin_catch → __cxa_begin_catch.
601 for (auto &tokenUsers :
602 llvm::make_early_inc_range(op.getCatchToken().getUses())) {
603 if (auto endOp =
604 mlir::dyn_cast<cir::EndCatchOp>(tokenUsers.getOwner())) {
605 builder.setInsertionPoint(endOp);
606 cir::CallOp::create(builder, endOp.getLoc(),
607 mlir::FlatSymbolRefAttr::get(endCatchFunc),
608 voidType, mlir::ValueRange{});
609 endOp.erase();
610 }
611 }
612
613 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
614 builder.setInsertionPoint(op);
615 auto callOp = cir::CallOp::create(
616 builder, op.getLoc(), mlir::FlatSymbolRefAttr::get(beginCatchFunc),
617 u8PtrType, mlir::ValueRange{exnPtr});
618 mlir::Value castResult = callOp.getResult();
619 mlir::Type expectedPtrType = op.getExnPtr().getType();
620 if (castResult.getType() != expectedPtrType)
621 castResult =
622 cir::CastOp::create(builder, op.getLoc(), expectedPtrType,
623 cir::CastKind::bitcast, callOp.getResult());
624 op.getExnPtr().replaceAllUsesWith(castResult);
625 op.erase();
626 } else if (auto op = mlir::dyn_cast<cir::ConstructCatchParamOp>(user)) {
627 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
628 if (mlir::failed(lowerConstructCatchParam(op, exnPtr)))
629 return mlir::failure();
630 } else if (mlir::isa<cir::EhDispatchOp>(user)) {
631 // The dispatch's catch types were already read into every reaching
632 // landing pad (read-only, before this traversal). The dispatch op
633 // itself is lowered once by the caller after all initiates are
634 // processed, so nothing is done here; the successor catch-handler
635 // blocks are still reached via the block-argument registration above.
636 } else if (auto op = mlir::dyn_cast<cir::EhTerminateOp>(user)) {
637 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
638 ensureClangCallTerminate(op.getLoc());
639 builder.setInsertionPoint(op);
640 auto call = cir::CallOp::create(
641 builder, op.getLoc(),
642 mlir::FlatSymbolRefAttr::get(clangCallTerminateFunc), voidType,
643 mlir::ValueRange{exnPtr});
644 call.setNothrowAttr(builder.getUnitAttr());
645 call->setAttr(cir::CIRDialect::getNoReturnAttrName(),
646 builder.getUnitAttr());
647 cir::UnreachableOp::create(builder, op.getLoc());
648 op.erase();
649 } else if (auto op = mlir::dyn_cast<cir::EhUnexpectedOp>(user)) {
650 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
651 ensureCxaCallUnexpectedDecl(op.getLoc());
652 builder.setInsertionPoint(op);
653 auto call = cir::CallOp::create(
654 builder, op.getLoc(),
655 mlir::FlatSymbolRefAttr::get(cxaCallUnexpectedFunc), voidType,
656 mlir::ValueRange{exnPtr});
657 call->setAttr(cir::CIRDialect::getNoReturnAttrName(),
658 builder.getUnitAttr());
659 cir::UnreachableOp::create(builder, op.getLoc());
660 op.erase();
661 } else if (auto op = mlir::dyn_cast<cir::ResumeOp>(user)) {
662 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
663 builder.setInsertionPoint(op);
664 cir::ResumeFlatOp::create(builder, op.getLoc(), exnPtr, typeId);
665 op.erase();
666 } else if (auto op = mlir::dyn_cast<cir::BrOp>(user)) {
667 // Replace eh_token operands with the (ptr, u32) pair.
668 SmallVector<mlir::Value> newOperands;
669 bool changed = false;
670 for (mlir::Value operand : op.getDestOperands()) {
671 auto it = ehTokenMap.find(operand);
672 if (it != ehTokenMap.end()) {
673 newOperands.push_back(it->second.first);
674 newOperands.push_back(it->second.second);
675 changed = true;
676 } else {
677 newOperands.push_back(operand);
678 }
679 }
680 if (changed) {
681 builder.setInsertionPoint(op);
682 cir::BrOp::create(builder, op.getLoc(), op.getDest(), newOperands);
683 op.erase();
684 }
685 }
686 }
687 }
688
689 initiateOp.erase();
690 return mlir::success();
691}
692
693/// Lower a cir.eh.dispatch by creating a comparison chain in new blocks.
694/// The dispatch itself is replaced with a branch to the first comparison
695/// block and then erased.
696void ItaniumEHLowering::lowerDispatch(cir::EhDispatchOp dispatch,
697 mlir::Value exnPtr, mlir::Value typeId) {
698 mlir::Location dispLoc = dispatch.getLoc();
699 mlir::Block *defaultDest = dispatch.getDefaultDestination();
700 mlir::Block *dispatchBlock = dispatch->getBlock();
701
702 llvm::SmallVector<mlir::Attribute> catchAttrs;
703 llvm::SmallVector<mlir::Block *> catchDests;
704 cir::EhFilterAttr filterAttr;
705 mlir::Block *filterDest = nullptr;
706 if (mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr()) {
707 for (auto [attr, dest] :
708 llvm::zip(catchTypes, dispatch.getCatchDestinations())) {
709 if (auto filter = mlir::dyn_cast<cir::EhFilterAttr>(attr)) {
710 assert(!filterAttr && "at most one filter handler");
711 filterAttr = filter;
712 filterDest = dest;
713 continue;
714 }
715 catchAttrs.push_back(attr);
716 catchDests.push_back(dest);
717 }
718 }
719
720 // Build the comparison chain in new blocks inserted after the dispatch's
721 // block. The dispatch itself is replaced with a branch to the first
722 // comparison block and erased below.
723 mlir::Block *insertBefore = dispatchBlock->getNextNode();
724 mlir::Block *falseDest = defaultDest;
725 if (filterAttr) {
726 assert(filterDest && "filter handler requires a destination");
727 // An empty permitted-type list means that every exception violates the
728 // specification, so the filter destination is taken unconditionally.
729 if (filterAttr.getPermittedTypes().empty()) {
730 falseDest = filterDest;
731 } else {
732 // The personality reports a filter failure with a negative selector.
733 // Type ids are still !u32i on the eh_token replacement pair, so recast
734 // to !s32i for the signed comparison. TODO: produce !s32i throughout.
735 auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type},
736 {dispLoc, dispLoc});
737 mlir::Value cmpExnPtr = cmpBlock->getArgument(0);
738 mlir::Value cmpTypeId = cmpBlock->getArgument(1);
739 mlir::Value signedTypeId = cir::CastOp::create(
740 builder, dispLoc, s32Type, cir::CastKind::integral, cmpTypeId);
741 mlir::Value zero = cir::ConstantOp::create(builder, dispLoc,
742 cir::IntAttr::get(s32Type, 0));
743 auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::lt,
744 signedTypeId, zero);
745 cir::BrCondOp::create(builder, dispLoc, cmpOp, filterDest, defaultDest,
746 mlir::ValueRange{cmpExnPtr, cmpTypeId},
747 mlir::ValueRange{cmpExnPtr, cmpTypeId});
748 insertBefore = cmpBlock;
749 falseDest = cmpBlock;
750 }
751 }
752
753 mlir::Block *firstCmpBlock = nullptr;
754 if (!catchAttrs.empty()) {
755 unsigned numCatches = catchAttrs.size();
756 for (int i = numCatches - 1; i >= 0; --i) {
757 auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type},
758 {dispLoc, dispLoc});
759
760 mlir::Value cmpExnPtr = cmpBlock->getArgument(0);
761 mlir::Value cmpTypeId = cmpBlock->getArgument(1);
762
763 auto globalView = mlir::cast<cir::GlobalViewAttr>(catchAttrs[i]);
764 auto ehTypeIdOp =
765 cir::EhTypeIdOp::create(builder, dispLoc, globalView.getSymbol());
766 auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::eq,
767 cmpTypeId, ehTypeIdOp.getTypeId());
768
769 cir::BrCondOp::create(builder, dispLoc, cmpOp, catchDests[i], falseDest,
770 mlir::ValueRange{cmpExnPtr, cmpTypeId},
771 mlir::ValueRange{cmpExnPtr, cmpTypeId});
772
773 insertBefore = cmpBlock;
774 falseDest = cmpBlock;
775 firstCmpBlock = cmpBlock;
776 }
777 } else {
778 firstCmpBlock = falseDest;
779 }
780
781 builder.setInsertionPoint(dispatch);
782 cir::BrOp::create(builder, dispLoc, firstCmpBlock,
783 mlir::ValueRange{exnPtr, typeId});
784
785 // The caller lowers each dispatch exactly once after every initiate has been
786 // processed, so no sibling still needs it; erase it now.
787 dispatch.erase();
788}
789
790mlir::FailureOr<cir::FuncOp>
791ItaniumEHLowering::resolveCatchCopyThunk(cir::ConstructCatchParamOp op) {
792 mlir::FlatSymbolRefAttr thunkRef = op.getCopyFnAttr();
793 mlir::StringAttr thunkName = thunkRef.getAttr();
794 auto cached = catchCopyThunks.find(thunkName);
795 if (cached != catchCopyThunks.end())
796 return cached->second;
797
798 cir::FuncOp thunk = mod.lookupSymbol<cir::FuncOp>(thunkRef);
799 if (!thunk)
800 return op.emitError("could not resolve catch-copy thunk symbol");
801 assert(thunk->hasAttr(cir::CIRDialect::getCatchCopyThunkAttrName()) &&
802 "verifier should have rejected non-thunk catch-copy reference");
803 if (thunk.isDeclaration())
804 return op.emitError("catch-copy thunk has no body to inline");
805
806 mlir::Region &thunkRegion = thunk.getRegion();
807 if (!llvm::hasSingleElement(thunkRegion))
808 return op.emitError("multi-block catch-copy thunks are NYI");
809
810 mlir::Block &thunkEntry = thunkRegion.front();
811 assert(thunkEntry.getNumArguments() == 2 &&
812 "catch-copy thunk must have exactly two parameters");
813 if (!mlir::isa<cir::ReturnOp>(thunkEntry.getTerminator()))
814 return op.emitError("catch-copy thunk must end in cir.return");
815
816 catchCopyThunks[thunkName] = thunk;
817 return thunk;
818}
819
820/// Lower a cir.construct_catch_param into the Itanium-specific sequence
821/// that runs before `__cxa_begin_catch` to bind the catch parameter to the
822/// in-flight exception.
823mlir::LogicalResult
824ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op,
825 mlir::Value exnPtr) {
826 mlir::Location loc = op.getLoc();
827 mlir::Value paramAddr = op.getParamAddr();
828 cir::PointerType paramAddrType =
829 mlir::cast<cir::PointerType>(paramAddr.getType());
830
831 if (op.getKind() == cir::InitCatchKind::Reference) {
833 constexpr unsigned headerSize = 32;
834
835 builder.setInsertionPoint(op);
836 auto index = cir::ConstantOp::create(
837 builder, loc, cir::IntAttr::get(u32Type, headerSize));
838 assert((exnPtr.getType() == voidPtrType || exnPtr.getType() == u8PtrType) &&
839 "lowerConstructCatchParam exn ptr not void* or i8*");
840 auto exnObj =
841 cir::PtrStrideOp::create(builder, loc, exnPtr.getType(), exnPtr, index);
842 mlir::Value casted =
843 cir::CastOp::create(builder, loc, paramAddrType.getPointee(),
844 cir::CastKind::bitcast, exnObj);
845 cir::StoreOp::create(builder, loc, casted, paramAddr, {}, {}, {}, {}, {});
846 op.erase();
847 return success();
848 }
849
850 if (op.getKind() != cir::InitCatchKind::NonTrivialCopy)
851 return op.emitError(
852 "ConstructCatchParam: only non_trivial_copy is supported");
853
854 ensureRuntimeDecls(loc);
855 ensureClangCallTerminate(loc);
856
857 // Call __cxa_get_exception_ptr to get the in-flight exception.
858 builder.setInsertionPoint(op);
859 cir::CallOp getExnCall = cir::CallOp::create(
860 builder, loc, mlir::FlatSymbolRefAttr::get(getExceptionPtrFunc),
861 u8PtrType, mlir::ValueRange{exnPtr});
862 getExnCall.setNothrowAttr(builder.getUnitAttr());
863 mlir::Value adjusted =
864 cir::CastOp::create(builder, loc, paramAddrType, cir::CastKind::bitcast,
865 getExnCall.getResult());
866
867 // Get the thunk function definition.
868 mlir::FailureOr<cir::FuncOp> thunkOr = resolveCatchCopyThunk(op);
869 if (mlir::failed(thunkOr))
870 return mlir::failure();
871 cir::FuncOp thunk = *thunkOr;
872
873 // This is also verified by resolveCatchCopyThunk, but the loop below is
874 // where the constraint is required so let's assert it again here.
875 assert(llvm::hasSingleElement(thunk.getRegion()) &&
876 "multi-block catch-copy thunks are NYI");
877
878 // Clone the thunk function to perform the copy.
879 mlir::Block &thunkEntry = thunk.getRegion().front();
880 mlir::IRMapping mapping;
881 mapping.map(thunkEntry.getArgument(0), paramAddr);
882 mapping.map(thunkEntry.getArgument(1), adjusted);
883 llvm::SmallVector<cir::CallOp> throwingCalls;
884 for (mlir::Operation &thunkOp : thunkEntry.without_terminator()) {
885 mlir::Operation *cloned = builder.clone(thunkOp, mapping);
886 if (cir::CallOp callOp = mlir::dyn_cast<cir::CallOp>(cloned))
887 if (!callOp.getNothrow())
888 throwingCalls.push_back(callOp);
889 }
890 op.erase();
891
892 if (throwingCalls.empty())
893 return mlir::success();
894
895 // All calls in the copy (which is usually just a single call) need to
896 // unwind to a terminate block if it throws an exception.
897 mlir::IRRewriter rewriter(builder);
898 mlir::Block *terminateBlock = nullptr;
899 for (cir::CallOp call : throwingCalls) {
900 if (!terminateBlock)
901 terminateBlock = buildTerminateBlock(call->getParentOfType<cir::FuncOp>(),
902 call.getLoc());
903 cir::replaceCallWithTryCall(call, terminateBlock, call.getLoc(), rewriter);
904 }
905 return mlir::success();
906}
907
908/// Lower a cir.try_throw to a cir.try_call of __cxa_throw (or
909/// __cxa_rethrow for the no-operand rethrow form). Materializes the
910/// type_info and dtor pointers from their symbol attributes, bitcasting
911/// each to !cir.ptr<!void> as required by the runtime function signature.
912mlir::LogicalResult ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
913 mlir::Location loc = op.getLoc();
914 mlir::Block *normalDest = op.getNormalDest();
915 mlir::Block *unwindDest = op.getUnwindDest();
916 builder.setInsertionPoint(op);
917
918 if (op.rethrows()) {
919 ensureCxaRethrowDecl(loc);
920 cir::TryCallOp::create(
921 builder, loc, mlir::FlatSymbolRefAttr::get(cxaRethrowFunc), voidType,
922 normalDest, unwindDest, mlir::ValueRange{});
923 op.erase();
924 return mlir::success();
925 }
926
927 ensureCxaThrowDecl(loc);
928
929 // Bitcast the exception pointer to void* if necessary.
930 mlir::Value exnPtr = op.getExceptionPtr();
931 if (exnPtr.getType() != voidPtrType)
932 exnPtr = cir::CastOp::create(builder, loc, voidPtrType,
933 cir::CastKind::bitcast, exnPtr);
934
935 // Materialize the type_info pointer, looking up the typed symbol in the
936 // module so we get the correct pointer type for cir.get_global, then
937 // bitcasting to void* to match the runtime signature.
938 mlir::FlatSymbolRefAttr typeInfoAttr = op.getTypeInfoAttr();
939 auto typeInfoGlobal = mod.lookupSymbol<cir::GlobalOp>(typeInfoAttr);
940 if (!typeInfoGlobal)
941 return op.emitError("type_info symbol not found in module");
942 auto typeInfoPtrTy = cir::PointerType::get(typeInfoGlobal.getSymType());
943 mlir::Value typeInfo = cir::GetGlobalOp::create(builder, loc, typeInfoPtrTy,
944 typeInfoAttr.getValue());
945 if (typeInfo.getType() != voidPtrType)
946 typeInfo = cir::CastOp::create(builder, loc, voidPtrType,
947 cir::CastKind::bitcast, typeInfo);
948
949 // Materialize the dtor pointer (or null if no dtor).
950 mlir::Value dtor;
951 if (mlir::FlatSymbolRefAttr dtorAttr = op.getDtorAttr()) {
952 auto dtorFunc = mod.lookupSymbol<cir::FuncOp>(dtorAttr);
953 if (!dtorFunc)
954 return op.emitError("dtor symbol not found in module");
955 auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
956 dtor =
957 cir::GetGlobalOp::create(builder, loc, dtorPtrTy, dtorAttr.getValue());
958 if (dtor.getType() != voidPtrType)
959 dtor = cir::CastOp::create(builder, loc, voidPtrType,
960 cir::CastKind::bitcast, dtor);
961 } else {
962 dtor = cir::ConstantOp::create(
963 builder, loc,
964 cir::ConstPtrAttr::get(voidPtrType, builder.getI64IntegerAttr(0)));
965 }
966
967 cir::TryCallOp::create(
968 builder, loc, mlir::FlatSymbolRefAttr::get(cxaThrowFunc), voidType,
969 normalDest, unwindDest, mlir::ValueRange{exnPtr, typeInfo, dtor});
970 op.erase();
971 return mlir::success();
972}
973
974/// Lower a cir.init_catch_param into the Itanium-specific sequence that
975/// materializes the catch parameter's local variable from the exception
976/// pointer returned by __cxa_begin_catch. The shape of the lowering
977/// depends on the init catch kind:
978///
979/// - Reference: the begin_catch result is
980/// the pointer value itself, so just bitcast and store it into the alloca
981/// except if it reference of pointer of record.
982/// - Pointer: the begin_catch result is
983/// the pointer value itself, so just bitcast and store it into the
984/// alloca.
985/// - Scalar (any other by-value catch): treat the begin_catch result as a
986/// pointer to the value, load it, and store it into the alloca.
987/// - Objc: Handle pointer representation with ObjCLifetime.
988/// - TrivialCopy: copy the exception
989/// object's bytes into the alloca via cir.copy.
990/// - NonTrivialCopy: the construction was already performed by the
991/// companion `cir.construct_catch_param` before `cir.begin_catch`, so
992/// this lowering is a no-op.
993///
994void ItaniumEHLowering::lowerInitCatchParam(cir::InitCatchParamOp op) {
995 builder.setInsertionPoint(op);
996 mlir::Location loc = op.getLoc();
997 mlir::Value exnPtr = op.getExnPtr();
998 mlir::Value paramAddr = op.getParamAddr();
999 auto paramAddrType = mlir::cast<cir::PointerType>(paramAddr.getType());
1000 mlir::Type elementType = paramAddrType.getPointee();
1001 cir::InitCatchKind kind = op.getKind();
1002
1003 switch (kind) {
1004 case InitCatchKind::Reference: {
1005 // We have no way to tell the personality function that we're
1006 // catching by reference, so if we're catching a pointer,
1007 // __cxa_begin_catch will actually return that pointer by value.
1008 if (const auto ref = mlir::dyn_cast<cir::PointerType>(elementType)) {
1009 // When catching by reference, generally we should just ignore
1010 // this by-value pointer and use the exception object instead.
1011 if (auto ptr = mlir::dyn_cast<cir::PointerType>(ref.getPointee()))
1012 if (!mlir::isa<cir::RecordType>(ptr.getPointee()))
1013 // Extracting and storing the actual exception object was performed by
1014 // cir.construct_catch_param before cir.begin_catch.
1015 break;
1016 }
1017
1018 mlir::Value casted = cir::CastOp::create(builder, loc, elementType,
1019 cir::CastKind::bitcast, exnPtr);
1020 cir::StoreOp::create(builder, loc, casted, paramAddr, {}, {}, {}, {}, {});
1021 break;
1022 }
1023 case InitCatchKind::TrivialCopy: {
1024 mlir::Value srcPtr = cir::CastOp::create(builder, loc, paramAddrType,
1025 cir::CastKind::bitcast, exnPtr);
1026 cir::CopyOp::create(builder, loc, paramAddr, srcPtr, {}, {});
1027 break;
1028 }
1029 case InitCatchKind::NonTrivialCopy:
1030 // The non-trivial copy was performed by the matching
1031 // cir.construct_catch_param before cir.begin_catch.
1032 break;
1033 case InitCatchKind::Scalar: {
1034 // Scalar by-value catch (integer, float, complex, etc.). The begin_catch
1035 // result points into the exception object; load the value through a
1036 // typed pointer and store it into the alloca.
1037 mlir::Value srcPtr = cir::CastOp::create(builder, loc, paramAddrType,
1038 cir::CastKind::bitcast, exnPtr);
1039 auto loadOp = cir::LoadOp::create(builder, loc, elementType, srcPtr);
1040 cir::StoreOp::create(builder, loc, loadOp.getResult(), paramAddr, {}, {},
1041 {}, {}, {});
1042 break;
1043 }
1044 case InitCatchKind::Pointer: {
1045 mlir::Value casted = cir::CastOp::create(builder, loc, elementType,
1046 cir::CastKind::bitcast, exnPtr);
1047 cir::StoreOp::create(builder, loc, casted, paramAddr, {}, {}, {}, {}, {});
1048 break;
1049 }
1050 case InitCatchKind::Objc:
1051 llvm_unreachable("InitCatchParam: ObjCLifetime is NYI");
1052 break;
1053 }
1054
1055 op.erase();
1056}
1057
1058//===----------------------------------------------------------------------===//
1059// The Pass
1060//===----------------------------------------------------------------------===//
1061
1062struct CIREHABILoweringPass
1063 : public impl::CIREHABILoweringBase<CIREHABILoweringPass> {
1064 CIREHABILoweringPass() = default;
1065 void runOnOperation() override;
1066};
1067
1068/// Erase all catch-init thunks after the EHABI lowering. CIRGen emits a thunk
1069/// for every `cir.construct_catch_param` op, but those uses should all have
1070/// been replaced during the lowering.
1071static void eraseCatchCopyThunks(mlir::ModuleOp mod) {
1072 llvm::StringRef catchHelperAttr =
1073 cir::CIRDialect::getCatchCopyThunkAttrName();
1074 for (cir::FuncOp f : llvm::make_early_inc_range(mod.getOps<cir::FuncOp>())) {
1075 if (!f->hasAttr(catchHelperAttr))
1076 continue;
1077 // This is an expensive check, so we need to rely on the implementation
1078 // to have done the right thing.
1079 assert(mlir::SymbolTable::symbolKnownUseEmpty(f, mod) &&
1080 "catch-init helper has remaining users");
1081 f.erase();
1082 }
1083}
1084
1085void CIREHABILoweringPass::runOnOperation() {
1086 auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
1087
1088 // The target triple is attached to the module as the "cir.triple"
1089 // attribute. If it is absent (e.g. a CIR module parsed from text without a
1090 // triple) we cannot determine the ABI and must skip the pass.
1091 auto tripleAttr = mlir::dyn_cast_if_present<mlir::StringAttr>(
1092 mod->getAttr(cir::CIRDialect::getTripleAttrName()));
1093 if (!tripleAttr) {
1094 mod.emitError("Module has no target triple");
1095 return;
1096 }
1097
1098 // Select the ABI-specific lowering handler from the triple. The Microsoft
1099 // C++ ABI targets a Windows MSVC environment; everything else uses Itanium.
1100 // Extend this when Microsoft ABI lowering is added.
1101 llvm::Triple triple(tripleAttr.getValue());
1102 std::unique_ptr<EHABILowering> lowering;
1103 if (triple.isWindowsMSVCEnvironment()) {
1104 mod.emitError(
1105 "EH ABI lowering is not yet implemented for the Microsoft ABI");
1106 return signalPassFailure();
1107 } else {
1108 lowering = std::make_unique<ItaniumEHLowering>(mod);
1109 }
1110
1111 if (mlir::failed(lowering->run()))
1112 return signalPassFailure();
1113
1114 // Sweep away any the thunk functions. They've been inlined to all users now.
1115 eraseCatchCopyThunks(mod);
1116}
1117
1118} // namespace
1119
1120std::unique_ptr<Pass> mlir::createCIREHABILoweringPass() {
1121 return std::make_unique<CIREHABILoweringPass>();
1122}
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
mlir::Block * replaceCallWithTryCall(cir::CallOp callOp, mlir::Block *unwindDest, mlir::Location loc, mlir::RewriterBase &rewriter)
Replace a cir::CallOp with a cir::TryCallOp whose unwind destination is unwindDest.
void info(bool Verbose, unsigned Level, const char *Fmt, Ts &&...Args)
Prints an indented note to stderr when Verbose is set.
Definition Utils.h:57
ASTEdit insertBefore(RangeSelector S, TextGenerator Replacement)
Inserts Replacement before S, leaving the source selected by \S unchanged.
Stencil run(MatchConsumer< std::string > C)
Wraps a MatchConsumer in a Stencil, so that it can be used in a Stencil.
Definition Stencil.cpp:489
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
std::unique_ptr< Pass > createCIREHABILoweringPass()
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)
static bool sizeOfUnwindException()