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// - personality function set on functions requiring EH
24//
25//===----------------------------------------------------------------------===//
26
27#include "PassDetail.h"
28#include "mlir/IR/Builders.h"
33#include "llvm/ADT/DenseMap.h"
34#include "llvm/ADT/SmallVector.h"
35#include "llvm/TargetParser/Triple.h"
36
37using namespace mlir;
38using namespace cir;
39
40namespace mlir {
41#define GEN_PASS_DEF_CIREHABILOWERING
42#include "clang/CIR/Dialect/Passes.h.inc"
43} // namespace mlir
44
45namespace {
46
47//===----------------------------------------------------------------------===//
48// Shared utilities
49//===----------------------------------------------------------------------===//
50
51/// Ensure a function with the given name and type exists in the module. If it
52/// does not exist, create a private external declaration.
53static cir::FuncOp getOrCreateRuntimeFuncDecl(mlir::ModuleOp mod,
54 mlir::Location loc,
55 StringRef name,
56 cir::FuncType funcTy) {
57 if (auto existing = mod.lookupSymbol<cir::FuncOp>(name))
58 return existing;
59
60 mlir::OpBuilder builder(mod.getContext());
61 builder.setInsertionPointToEnd(mod.getBody());
62 auto funcOp = cir::FuncOp::create(builder, loc, name, funcTy);
63 funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
64 funcOp.setPrivate();
65 return funcOp;
66}
67
68//===----------------------------------------------------------------------===//
69// EH ABI Lowering Base Class
70//===----------------------------------------------------------------------===//
71
72/// Abstract base class for exception-handling ABI lowering.
73/// Each supported ABI (Itanium, Microsoft, etc.) provides a concrete subclass.
74class EHABILowering {
75public:
76 explicit EHABILowering(mlir::ModuleOp mod)
77 : mod(mod), ctx(mod.getContext()), builder(ctx) {}
78 virtual ~EHABILowering() = default;
79
80 /// Lower all EH operations in the module to an ABI-specific form.
81 virtual mlir::LogicalResult run() = 0;
82
83protected:
84 mlir::ModuleOp mod;
85 mlir::MLIRContext *ctx;
86 mlir::OpBuilder builder;
87};
88
89//===----------------------------------------------------------------------===//
90// Itanium EH ABI Lowering
91//===----------------------------------------------------------------------===//
92
93/// Lowers flattened CIR EH operations to the Itanium C++ ABI form.
94///
95/// The entry point is run(), which iterates over all functions and
96/// calls lowerFunc() for each. lowerFunc() drives all lowering from
97/// cir.eh.initiate operations: every other EH op (begin/end_cleanup,
98/// eh.dispatch, begin/end_catch, resume) is reachable by tracing the
99/// eh_token produced by the initiate through its users.
100class ItaniumEHLowering : public EHABILowering {
101public:
102 using EHABILowering::EHABILowering;
103 mlir::LogicalResult run() override;
104
105private:
106 /// Maps a !cir.eh_token value to its Itanium ABI replacement pair:
107 /// an exception pointer (!cir.ptr<!void>) and a type id (!u32i).
108 using EhTokenMap = DenseMap<mlir::Value, std::pair<mlir::Value, mlir::Value>>;
109
110 cir::VoidType voidType;
111 cir::PointerType voidPtrType;
112 cir::PointerType u8PtrType;
113 cir::IntType u32Type;
114
115 // Cached runtime function declarations, initialized when needed by
116 // ensureRuntimeDecls().
117 cir::FuncOp personalityFunc;
118 cir::FuncOp beginCatchFunc;
119 cir::FuncOp endCatchFunc;
120 cir::FuncOp clangCallTerminateFunc;
121
122 constexpr const static ::llvm::StringLiteral kGxxPersonality =
123 "__gxx_personality_v0";
124
125 void ensureRuntimeDecls(mlir::Location loc);
126 void ensureClangCallTerminate(mlir::Location loc);
127 mlir::LogicalResult lowerFunc(cir::FuncOp funcOp);
128 void lowerEhInitiate(cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
129 SmallVectorImpl<mlir::Operation *> &deadOps);
130 void lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value exnPtr,
131 mlir::Value typeId,
132 SmallVectorImpl<mlir::Operation *> &deadOps);
133};
134
135/// Lower all EH operations in the module to the Itanium-specific form.
136mlir::LogicalResult ItaniumEHLowering::run() {
137 // Pre-compute the common types used throughout all function lowerings.
138 // TODO(cir): Move these to the base class if they are also needed for MSVC.
139 voidType = cir::VoidType::get(ctx);
140 voidPtrType = cir::PointerType::get(voidType);
141 auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false);
142 u8PtrType = cir::PointerType::get(u8Type);
143 u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false);
144
145 for (cir::FuncOp funcOp : mod.getOps<cir::FuncOp>()) {
146 if (mlir::failed(lowerFunc(funcOp)))
147 return mlir::failure();
148 }
149 return mlir::success();
150}
151
152/// Ensure the necessary Itanium runtime function declarations exist in the
153/// module.
154void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) {
155 // TODO(cir): Handle other personality functions. This probably isn't needed
156 // here if we fix codegen to always set the personality function.
157 if (!personalityFunc) {
158 auto s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true);
159 auto personalityFuncTy = cir::FuncType::get({}, s32Type, /*isVarArg=*/true);
160 personalityFunc = getOrCreateRuntimeFuncDecl(mod, loc, kGxxPersonality,
161 personalityFuncTy);
162 }
163
164 if (!beginCatchFunc) {
165 auto beginCatchFuncTy =
166 cir::FuncType::get({voidPtrType}, u8PtrType, /*isVarArg=*/false);
167 beginCatchFunc = getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_begin_catch",
168 beginCatchFuncTy);
169 }
170
171 if (!endCatchFunc) {
172 auto endCatchFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
173 endCatchFunc =
174 getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_end_catch", endCatchFuncTy);
175 }
176}
177
178/// Ensure the __clang_call_terminate function exists in the module. This
179/// function is defined with a body that calls __cxa_begin_catch followed by
180/// std::terminate, matching the behavior of Clang's LLVM IR codegen.
181///
182/// void __clang_call_terminate(void *exn) nounwind noreturn {
183/// __cxa_begin_catch(exn);
184/// std::terminate();
185/// unreachable;
186/// }
187void ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) {
188 if (clangCallTerminateFunc)
189 return;
190
191 ensureRuntimeDecls(loc);
192
193 if (auto existing = mod.lookupSymbol<cir::FuncOp>("__clang_call_terminate")) {
194 clangCallTerminateFunc = existing;
195 return;
196 }
197
198 auto funcTy = cir::FuncType::get({voidPtrType}, voidType, /*isVarArg=*/false);
199 builder.setInsertionPointToEnd(mod.getBody());
200 auto funcOp =
201 cir::FuncOp::create(builder, loc, "__clang_call_terminate", funcTy);
202 funcOp.setLinkage(cir::GlobalLinkageKind::LinkOnceODRLinkage);
203 funcOp.setGlobalVisibility(cir::VisibilityKind::Hidden);
204
205 mlir::Block *entryBlock = funcOp.addEntryBlock();
206 builder.setInsertionPointToStart(entryBlock);
207 mlir::Value exnArg = entryBlock->getArgument(0);
208
209 auto catchCall = cir::CallOp::create(
210 builder, loc, mlir::FlatSymbolRefAttr::get(beginCatchFunc), u8PtrType,
211 mlir::ValueRange{exnArg});
212 catchCall.setNothrowAttr(builder.getUnitAttr());
213
214 auto terminateFuncDecl = getOrCreateRuntimeFuncDecl(
215 mod, loc, "_ZSt9terminatev",
216 cir::FuncType::get({}, voidType, /*isVarArg=*/false));
217 terminateFuncDecl->setAttr(cir::CIRDialect::getNoReturnAttrName(),
218 builder.getUnitAttr());
219 auto terminateCall = cir::CallOp::create(
220 builder, loc, mlir::FlatSymbolRefAttr::get(terminateFuncDecl), voidType,
221 mlir::ValueRange{});
222 terminateCall.setNothrowAttr(builder.getUnitAttr());
223 terminateCall->setAttr(cir::CIRDialect::getNoReturnAttrName(),
224 builder.getUnitAttr());
225
226 cir::UnreachableOp::create(builder, loc);
227
228 funcOp->setAttr(cir::CIRDialect::getNoReturnAttrName(),
229 builder.getUnitAttr());
230 clangCallTerminateFunc = funcOp;
231}
232
233/// Lower all EH operations in a single function.
234mlir::LogicalResult ItaniumEHLowering::lowerFunc(cir::FuncOp funcOp) {
235 if (funcOp.isDeclaration())
236 return mlir::success();
237
238 // All EH lowering follows from cir.eh.initiate operations. The token each
239 // initiate produces connects it to every other EH op in the function
240 // (begin/end_cleanup, eh.dispatch, begin/end_catch, resume) through the
241 // token graph. A single walk to collect initiates is therefore sufficient.
242 SmallVector<cir::EhInitiateOp> initiateOps;
243 funcOp.walk([&](cir::EhInitiateOp op) { initiateOps.push_back(op); });
244 if (initiateOps.empty())
245 return mlir::success();
246
247 ensureRuntimeDecls(funcOp.getLoc());
248
249 // Set the personality function if it is not already set.
250 // TODO(cir): The personality function should already have been set by this
251 // point. If we've seen a try operation, it will have been set by
252 // emitCXXTryStmt. If we only have cleanups, it may not have been set. We
253 // need to fix that in CodeGen. This is a placeholder until that is done.
254 if (!funcOp.getPersonality())
255 funcOp.setPersonality(kGxxPersonality);
256
257 // Lower each initiate and all EH ops connected to it. The token map is
258 // shared across all initiate operations. Multiple initiates may flow into the
259 // same dispatch block, and the map ensures the arguments are registered
260 // only once. Dispatch ops are scheduled for deferred removal so that sibling
261 // initiates can still read catch types from a shared dispatch.
262 EhTokenMap ehTokenMap;
263 SmallVector<mlir::Operation *> deadOps;
264 for (cir::EhInitiateOp initiateOp : initiateOps)
265 lowerEhInitiate(initiateOp, ehTokenMap, deadOps);
266
267 // Erase operations that were deferred during per-initiate processing
268 // (dispatch ops whose catch types were read by multiple initiates).
269 for (mlir::Operation *op : deadOps)
270 op->erase();
271
272 // Remove the !cir.eh_token block arguments that were replaced by (ptr, u32)
273 // pairs. Iterate in reverse to preserve argument indices during removal.
274 for (mlir::Block &block : funcOp.getBody()) {
275 for (int i = block.getNumArguments() - 1; i >= 0; --i) {
276 if (mlir::isa<cir::EhTokenType>(block.getArgument(i).getType()))
277 block.eraseArgument(i);
278 }
279 }
280
281 return mlir::success();
282}
283
284/// Lower all EH operations connected to a single cir.eh.initiate.
285///
286/// The cir.eh.initiate is the root of a token graph. The token it produces
287/// flows through branch edges to consuming operations:
288///
289/// cir.eh.initiate → (via cir.br) → cir.begin_cleanup
290/// → cir.end_cleanup (via cleanup_token)
291/// → (via cir.br) → cir.eh.dispatch
292/// → (successors) →
293/// cir.begin_catch
294/// → cir.end_catch
295/// (via catch_token)
296/// → cir.resume
297///
298/// A single traversal of the token graph discovers and processes every
299/// connected op inline. The inflight_exception is created up-front without
300/// a catch_type_list; when the dispatch is encountered during traversal,
301/// the catch types are read and set on the inflight op.
302///
303/// Dispatch ops are not erased during per-initiate processing because they may
304/// be used by other initiate ops that haven't yet been lowered. Instead they
305/// are added to \p deadOps and erased by the caller after all initiates have
306/// been lowered.
307///
308/// \p ehTokenMap is shared across all initiates in the function so that block
309/// arguments reachable from multiple sibling initiates are registered once.
310void ItaniumEHLowering::lowerEhInitiate(
311 cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
312 SmallVectorImpl<mlir::Operation *> &deadOps) {
313 mlir::Value rootToken = initiateOp.getEhToken();
314
315 // Create the inflight_exception without a catch_type_list. The catch types
316 // will be set once we encounter the dispatch during the traversal below.
317 builder.setInsertionPoint(initiateOp);
318 auto inflightOp = cir::EhInflightOp::create(
319 builder, initiateOp.getLoc(), /*cleanup=*/initiateOp.getCleanup(),
320 /*catch_all=*/false,
321 /*catch_type_list=*/mlir::ArrayAttr{});
322
323 ehTokenMap[rootToken] = {inflightOp.getExceptionPtr(),
324 inflightOp.getTypeId()};
325
326 // Single traversal of the token graph. For each token value (the root token
327 // or a block argument that carries it), we snapshot its users, register
328 // (ptr, u32) replacement arguments on successor blocks, then process every
329 // user inline. This avoids collecting ops into separate vectors.
330 SmallVector<mlir::Value> worklist;
331 SmallPtrSet<mlir::Value, 8> visited;
332 worklist.push_back(rootToken);
333
334 while (!worklist.empty()) {
335 mlir::Value current = worklist.pop_back_val();
336 if (!visited.insert(current).second)
337 continue;
338
339 // Snapshot users before modifying any of them (erasing ops during
340 // iteration would invalidate the use-list iterator).
341 SmallVector<mlir::Operation *> users;
342 for (mlir::OpOperand &use : current.getUses())
343 users.push_back(use.getOwner());
344
345 // Register replacement block arguments on successor blocks (extending the
346 // worklist), then lower the op itself.
347 for (mlir::Operation *user : users) {
348 // Trace into successor blocks to register (ptr, u32) replacement
349 // arguments for any !cir.eh_token block arguments found there. Even
350 // if a block arg was already registered by a sibling initiate, it is
351 // still added to the worklist so that the traversal can reach the
352 // shared dispatch to read catch types.
353 for (unsigned s = 0; s < user->getNumSuccessors(); ++s) {
354 mlir::Block *succ = user->getSuccessor(s);
355 for (mlir::BlockArgument arg : succ->getArguments()) {
356 if (!mlir::isa<cir::EhTokenType>(arg.getType()))
357 continue;
358 if (!ehTokenMap.count(arg)) {
359 mlir::Value ptrArg = succ->addArgument(voidPtrType, arg.getLoc());
360 mlir::Value u32Arg = succ->addArgument(u32Type, arg.getLoc());
361 ehTokenMap[arg] = {ptrArg, u32Arg};
362 }
363 worklist.push_back(arg);
364 }
365 }
366
367 if (auto op = mlir::dyn_cast<cir::BeginCleanupOp>(user)) {
368 // begin_cleanup / end_cleanup are no-ops for Itanium. Erase the
369 // end_cleanup first (drops the cleanup_token use) then the begin.
370 for (auto &tokenUsers :
371 llvm::make_early_inc_range(op.getCleanupToken().getUses())) {
372 if (auto endOp =
373 mlir::dyn_cast<cir::EndCleanupOp>(tokenUsers.getOwner()))
374 endOp.erase();
375 }
376 op.erase();
377 } else if (auto op = mlir::dyn_cast<cir::BeginCatchOp>(user)) {
378 // Replace end_catch → __cxa_end_catch (drops the catch_token use),
379 // then replace begin_catch → __cxa_begin_catch.
380 for (auto &tokenUsers :
381 llvm::make_early_inc_range(op.getCatchToken().getUses())) {
382 if (auto endOp =
383 mlir::dyn_cast<cir::EndCatchOp>(tokenUsers.getOwner())) {
384 builder.setInsertionPoint(endOp);
385 cir::CallOp::create(builder, endOp.getLoc(),
386 mlir::FlatSymbolRefAttr::get(endCatchFunc),
387 voidType, mlir::ValueRange{});
388 endOp.erase();
389 }
390 }
391
392 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
393 builder.setInsertionPoint(op);
394 auto callOp = cir::CallOp::create(
395 builder, op.getLoc(), mlir::FlatSymbolRefAttr::get(beginCatchFunc),
396 u8PtrType, mlir::ValueRange{exnPtr});
397 mlir::Value castResult = callOp.getResult();
398 mlir::Type expectedPtrType = op.getExnPtr().getType();
399 if (castResult.getType() != expectedPtrType)
400 castResult =
401 cir::CastOp::create(builder, op.getLoc(), expectedPtrType,
402 cir::CastKind::bitcast, callOp.getResult());
403 op.getExnPtr().replaceAllUsesWith(castResult);
404 op.erase();
405 } else if (auto op = mlir::dyn_cast<cir::EhDispatchOp>(user)) {
406 // Read catch types from the dispatch and set them on the inflight op.
407 mlir::ArrayAttr catchTypes = op.getCatchTypesAttr();
408 if (catchTypes && catchTypes.size() > 0) {
409 SmallVector<mlir::Attribute> typeSymbols;
410 for (mlir::Attribute attr : catchTypes)
411 typeSymbols.push_back(
412 mlir::cast<cir::GlobalViewAttr>(attr).getSymbol());
413 inflightOp.setCatchTypeListAttr(builder.getArrayAttr(typeSymbols));
414 }
415 if (op.getDefaultIsCatchAll())
416 inflightOp.setCatchAllAttr(builder.getUnitAttr());
417 // Only lower the dispatch once. A sibling initiate sharing the same
418 // dispatch will still read its catch types (above), but the comparison
419 // chain and branch replacement are only created the first time.
420 if (!llvm::is_contained(deadOps, op.getOperation())) {
421 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
422 lowerDispatch(op, exnPtr, typeId, deadOps);
423 }
424 } else if (auto op = mlir::dyn_cast<cir::EhTerminateOp>(user)) {
425 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
426 ensureClangCallTerminate(op.getLoc());
427 builder.setInsertionPoint(op);
428 auto call = cir::CallOp::create(
429 builder, op.getLoc(),
430 mlir::FlatSymbolRefAttr::get(clangCallTerminateFunc), voidType,
431 mlir::ValueRange{exnPtr});
432 call.setNothrowAttr(builder.getUnitAttr());
433 call->setAttr(cir::CIRDialect::getNoReturnAttrName(),
434 builder.getUnitAttr());
435 cir::UnreachableOp::create(builder, op.getLoc());
436 op.erase();
437 } else if (auto op = mlir::dyn_cast<cir::ResumeOp>(user)) {
438 auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
439 builder.setInsertionPoint(op);
440 cir::ResumeFlatOp::create(builder, op.getLoc(), exnPtr, typeId);
441 op.erase();
442 } else if (auto op = mlir::dyn_cast<cir::BrOp>(user)) {
443 // Replace eh_token operands with the (ptr, u32) pair.
444 SmallVector<mlir::Value> newOperands;
445 bool changed = false;
446 for (mlir::Value operand : op.getDestOperands()) {
447 auto it = ehTokenMap.find(operand);
448 if (it != ehTokenMap.end()) {
449 newOperands.push_back(it->second.first);
450 newOperands.push_back(it->second.second);
451 changed = true;
452 } else {
453 newOperands.push_back(operand);
454 }
455 }
456 if (changed) {
457 builder.setInsertionPoint(op);
458 cir::BrOp::create(builder, op.getLoc(), op.getDest(), newOperands);
459 op.erase();
460 }
461 }
462 }
463 }
464
465 initiateOp.erase();
466}
467
468/// Lower a cir.eh.dispatch by creating a comparison chain in new blocks.
469/// The dispatch itself is replaced with a branch to the first comparison
470/// block and added to deadOps for deferred removal.
471void ItaniumEHLowering::lowerDispatch(
472 cir::EhDispatchOp dispatch, mlir::Value exnPtr, mlir::Value typeId,
473 SmallVectorImpl<mlir::Operation *> &deadOps) {
474 mlir::Location dispLoc = dispatch.getLoc();
475 mlir::Block *defaultDest = dispatch.getDefaultDestination();
476 mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr();
477 mlir::SuccessorRange catchDests = dispatch.getCatchDestinations();
478 mlir::Block *dispatchBlock = dispatch->getBlock();
479
480 // Build the comparison chain in new blocks inserted after the dispatch's
481 // block. The dispatch itself is replaced with a branch to the first
482 // comparison block and scheduled for deferred removal.
483 if (!catchTypes || catchTypes.empty()) {
484 // No typed catches: replace dispatch with a direct branch.
485 builder.setInsertionPoint(dispatch);
486 cir::BrOp::create(builder, dispLoc, defaultDest,
487 mlir::ValueRange{exnPtr, typeId});
488 } else {
489 unsigned numCatches = catchTypes.size();
490
491 // Create and populate comparison blocks in reverse order so that each
492 // block's false destination (the next comparison block, or defaultDest
493 // for the last one) is already available. Each createBlock inserts
494 // before the previous one, so the blocks end up in forward order.
495 mlir::Block *insertBefore = dispatchBlock->getNextNode();
496 mlir::Block *falseDest = defaultDest;
497 mlir::Block *firstCmpBlock = nullptr;
498 for (int i = numCatches - 1; i >= 0; --i) {
499 auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type},
500 {dispLoc, dispLoc});
501
502 mlir::Value cmpExnPtr = cmpBlock->getArgument(0);
503 mlir::Value cmpTypeId = cmpBlock->getArgument(1);
504
505 auto globalView = mlir::cast<cir::GlobalViewAttr>(catchTypes[i]);
506 auto ehTypeIdOp =
507 cir::EhTypeIdOp::create(builder, dispLoc, globalView.getSymbol());
508 auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::eq,
509 cmpTypeId, ehTypeIdOp.getTypeId());
510
511 cir::BrCondOp::create(builder, dispLoc, cmpOp, catchDests[i], falseDest,
512 mlir::ValueRange{cmpExnPtr, cmpTypeId},
513 mlir::ValueRange{cmpExnPtr, cmpTypeId});
514
515 insertBefore = cmpBlock;
516 falseDest = cmpBlock;
517 firstCmpBlock = cmpBlock;
518 }
519
520 // Replace the dispatch with a branch to the first comparison block.
521 builder.setInsertionPoint(dispatch);
522 cir::BrOp::create(builder, dispLoc, firstCmpBlock,
523 mlir::ValueRange{exnPtr, typeId});
524 }
525
526 // Schedule the dispatch for deferred removal. We cannot erase it now because
527 // a sibling initiate that shares this dispatch may still need to read its
528 // catch types.
529 deadOps.push_back(dispatch);
530}
531
532//===----------------------------------------------------------------------===//
533// The Pass
534//===----------------------------------------------------------------------===//
535
536struct CIREHABILoweringPass
537 : public impl::CIREHABILoweringBase<CIREHABILoweringPass> {
538 CIREHABILoweringPass() = default;
539 void runOnOperation() override;
540};
541
542void CIREHABILoweringPass::runOnOperation() {
543 auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
544
545 // The target triple is attached to the module as the "cir.triple" attribute.
546 // If it is absent (e.g. a CIR module parsed from text without a triple) we
547 // cannot determine the ABI and must skip the pass.
548 auto tripleAttr = mlir::dyn_cast_if_present<mlir::StringAttr>(
549 mod->getAttr(cir::CIRDialect::getTripleAttrName()));
550 if (!tripleAttr) {
551 mod.emitError("Module has no target triple");
552 return;
553 }
554
555 // Select the ABI-specific lowering handler from the triple. The Microsoft
556 // C++ ABI targets a Windows MSVC environment; everything else uses Itanium.
557 // Extend this when Microsoft ABI lowering is added.
558 llvm::Triple triple(tripleAttr.getValue());
559 std::unique_ptr<EHABILowering> lowering;
560 if (triple.isWindowsMSVCEnvironment()) {
561 mod.emitError(
562 "EH ABI lowering is not yet implemented for the Microsoft ABI");
563 return signalPassFailure();
564 } else {
565 lowering = std::make_unique<ItaniumEHLowering>(mod);
566 }
567
568 if (mlir::failed(lowering->run()))
569 return signalPassFailure();
570}
571
572} // namespace
573
574std::unique_ptr<Pass> mlir::createCIREHABILoweringPass() {
575 return std::make_unique<CIREHABILoweringPass>();
576}
__device__ __2f16 float __ockl_bool s
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)