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