clang 23.0.0git
CIRTransformUtils.cpp
Go to the documentation of this file.
1//===- CIRTransformUtils.cpp - Shared helpers for CIR transforms ----------===//
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
10
12
13#include "llvm/ADT/DepthFirstIterator.h"
14
15void cir::collectUnreachable(mlir::Operation *parent,
17 // For every region under `parent`, find the blocks unreachable from the
18 // entry via a forward CFG traversal and collect their ops.
19 llvm::df_iterator_default_set<mlir::Block *, 16> reachable;
20 parent->walk([&](mlir::Region *region) {
21 // Empty regions have no blocks; single-block regions have only the
22 // entry, which is trivially reachable. Either way, nothing to collect.
23 if (region->empty() || region->hasOneBlock())
24 return;
25
26 // We clear this for each region as we walk the parent because each block
27 // is only in one region, so the reachable blocks from previously visited
28 // regions aren't needed.
29 reachable.clear();
30
31 // The depth_first_ext range iterator internally adds each block to the
32 // reachable set as it visits it, so while this loop looks like it doesn't
33 // do anything, it's actually populating the set of reachable blocks in
34 // this region.
35 for (mlir::Block *blk : llvm::depth_first_ext(&region->front(), reachable))
36 (void)blk;
37
38 // Collect the unreachable blocks.
39 for (mlir::Block &blk : *region) {
40 if (reachable.contains(&blk))
41 continue;
42 for (mlir::Operation &op : blk)
43 ops.push_back(&op);
44 }
45 });
46}
47
48mlir::Block *cir::replaceCallWithTryCall(cir::CallOp callOp,
49 mlir::Block *unwindDest,
50 mlir::Location loc,
51 mlir::RewriterBase &rewriter) {
52 mlir::Block *callBlock = callOp->getBlock();
53
54 assert(!callOp.getNothrow() && "call is not expected to throw");
55
56 // Split the block after the call - remaining ops become the normal
57 // destination.
58 mlir::Block *normalDest =
59 rewriter.splitBlock(callBlock, std::next(callOp->getIterator()));
60
61 // Build the try_call to replace the original call.
62 rewriter.setInsertionPoint(callOp);
63 cir::TryCallOp tryCallOp;
64 if (callOp.isIndirect()) {
65 mlir::Value indTarget = callOp.getIndirectCall();
66 auto ptrTy = mlir::cast<cir::PointerType>(indTarget.getType());
67 auto resTy = mlir::cast<cir::FuncType>(ptrTy.getPointee());
68 tryCallOp =
69 cir::TryCallOp::create(rewriter, loc, indTarget, resTy, normalDest,
70 unwindDest, callOp.getArgOperands());
71 } else {
72 mlir::Type resType = callOp->getNumResults() > 0
73 ? callOp->getResult(0).getType()
74 : mlir::Type();
75 tryCallOp =
76 cir::TryCallOp::create(rewriter, loc, callOp.getCalleeAttr(), resType,
77 normalDest, unwindDest, callOp.getArgOperands());
78 }
79
80 // Copy all attributes from the original call except those already set by
81 // TryCallOp::create or that are operation-specific and should not be copied.
82 llvm::StringRef excludedAttrs[] = {
83 cir::CIRDialect::getCalleeAttrName(), // Set by create()
84 cir::CIRDialect::getOperandSegmentSizesAttrName(),
85 };
86 for (mlir::NamedAttribute attr : callOp->getAttrs()) {
87 if (llvm::is_contained(excludedAttrs, attr.getName()))
88 continue;
89 assert(!llvm::is_contained(
90 {
91 cir::CIRDialect::getNoThrowAttrName(),
92 cir::CIRDialect::getNoUnwindAttrName(),
93 },
94 attr.getName()) &&
95 "unexpected attribute on converted call");
96 tryCallOp->setAttr(attr.getName(), attr.getValue());
97 }
98
99 // Replace uses of the call result with the try_call result. Use the
100 // rewriter API so any listener (e.g. the pattern rewriter in
101 // FlattenCFG) is notified of the in-place modifications to each user.
102 if (callOp->getNumResults() > 0)
103 rewriter.replaceAllUsesWith(callOp->getResult(0), tryCallOp.getResult());
104
105 rewriter.eraseOp(callOp);
106 return normalDest;
107}
108
109mlir::Block *cir::replaceThrowWithTryThrow(cir::ThrowOp throwOp,
110 mlir::Block *unwindDest,
111 mlir::Location loc,
112 mlir::RewriterBase &rewriter) {
113 // The throw never returns, so the try_throw's normal destination is
114 // literally unreachable. Place it at the end of the parent function
115 // rather than splitting it out of the throw's block in the middle of
116 // the normal control flow.
117 auto funcOp = throwOp->getParentOfType<cir::FuncOp>();
118 assert(funcOp && "throw must be inside a function");
119 mlir::Region &body = funcOp.getBody();
120
121 mlir::Block *normalDest;
122 {
123 mlir::OpBuilder::InsertionGuard guard(rewriter);
124 normalDest = rewriter.createBlock(&body, body.end());
125 cir::UnreachableOp::create(rewriter, loc);
126 }
127
128 // Build the try_throw to replace the original throw.
129 rewriter.setInsertionPoint(throwOp);
130 auto tryThrowOp = cir::TryThrowOp::create(
131 rewriter, loc, throwOp.getExceptionPtr(), throwOp.getTypeInfoAttr(),
132 throwOp.getDtorAttr(), normalDest, unwindDest);
133
134 // Copy any extra attributes from the original throw. The type_info and
135 // dtor attributes are already set by TryThrowOp::create above.
136 llvm::StringRef excludedAttrs[] = {
137 "type_info",
138 "dtor",
139 };
140 for (mlir::NamedAttribute attr : throwOp->getAttrs()) {
141 if (llvm::is_contained(excludedAttrs, attr.getName()))
142 continue;
143 tryThrowOp->setAttr(attr.getName(), attr.getValue());
144 }
145
146 // Erase the throw along with any operations that followed it in its
147 // parent block (typically a cir.unreachable left over from CIR codegen).
148 // They must be removed because try_throw is a terminator and a block
149 // can have only one terminator.
150 mlir::Block *throwBlock = throwOp->getBlock();
151 while (&throwBlock->back() != tryThrowOp)
152 rewriter.eraseOp(&throwBlock->back());
153
154 return normalDest;
155}
void collectUnreachable(mlir::Operation *parent, llvm::SmallVectorImpl< mlir::Operation * > &ops)
Collect ops in blocks that are unreachable from their region's entry, appending them to ops.
mlir::Block * replaceThrowWithTryThrow(cir::ThrowOp throwOp, mlir::Block *unwindDest, mlir::Location loc, mlir::RewriterBase &rewriter)
Replace a cir::ThrowOp with a cir::TryThrowOp whose unwind destination is unwindDest.
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.