clang 24.0.0git
GotoSolver.cpp
Go to the documentation of this file.
1//====- GotoSolver.cpp -----------------------------------===//
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#include "PassDetail.h"
11#include "llvm/ADT/SetVector.h"
12#include "llvm/ADT/StringMap.h"
13#include "llvm/Support/TimeProfiler.h"
14#include <memory>
15
16using namespace mlir;
17using namespace cir;
18
19namespace mlir {
20#define GEN_PASS_DEF_GOTOSOLVER
21#include "clang/CIR/Dialect/Passes.h.inc"
22} // namespace mlir
23
24namespace {
25
26struct GotoSolverPass : public impl::GotoSolverBase<GotoSolverPass> {
27 GotoSolverPass() = default;
28 void runOnOperation() override;
29};
30
31static void process(cir::FuncOp func,
32 llvm::ArrayRef<StringRef> globalBlockAddrLabels) {
33 mlir::OpBuilder rewriter(func.getContext());
34 llvm::StringMap<Block *> labels;
37 // Address-taken labels in a deterministic order: those referenced from global
38 // initializers first (in initializer order), then those taken by a
39 // cir.block_address op (in IR order). A label may be named more than once (a
40 // dispatch table can list it twice); a block only needs to be a successor
41 // once, so keep the first occurrence.
42 llvm::SmallSetVector<StringRef, 4> addrTakenLabels(llvm::from_range,
43 globalBlockAddrLabels);
44
45 func.getBody().walk([&](mlir::Operation *op) {
46 if (auto lab = dyn_cast<cir::LabelOp>(op)) {
47 labels.try_emplace(lab.getLabel(), lab->getBlock());
48 } else if (auto goTo = dyn_cast<cir::GotoOp>(op)) {
49 gotos.push_back(goTo);
50 } else if (auto indirect = dyn_cast<cir::IndirectGotoOp>(op)) {
51 indirectGotos.push_back(indirect);
52 } else if (auto blockAddr = dyn_cast<cir::BlockAddressOp>(op)) {
53 addrTakenLabels.insert(blockAddr.getBlockAddrInfo().getLabel());
54 }
55 });
56
57 // Drop LabelOps whose address is never taken; the rest may be indirect-branch
58 // successors and must survive.
59 for (auto &lab : labels) {
60 if (!addrTakenLabels.contains(lab.getKey())) {
61 if (auto labelOp = dyn_cast<cir::LabelOp>(&lab.getValue()->front()))
62 labelOp.erase();
63 }
64 }
65
66 // Resolve regular symbolic gotos to direct branches.
67 for (auto goTo : gotos) {
68 mlir::OpBuilder::InsertionGuard guard(rewriter);
69 rewriter.setInsertionPoint(goTo);
70 Block *dest = labels[goTo.getLabel()];
71 cir::BrOp::create(rewriter, goTo.getLoc(), dest);
72 goTo.erase();
73 }
74
75 // A label whose address is merely taken still emits its address constant; an
76 // indirect branch is only needed when the function actually branches with a
77 // `goto *expr`.
78 if (indirectGotos.empty())
79 return;
80
81 // Resolve indirect gotos. FlattenCFG has already merged the nested scopes
82 // into one region, so the shared indirect-branch block and its successors all
83 // live in func's body now -- the cross-region branch that broke a nested
84 // `goto *` during CIRGen cannot arise here.
85 // The shared block represents every `goto *expr` that funnels into it, so
86 // fuse their locations.
88 for (cir::IndirectGotoOp indirect : indirectGotos)
89 gotoLocs.push_back(indirect.getLoc());
90 mlir::Location loc = mlir::FusedLoc::get(func.getContext(), gotoLocs);
91 mlir::Type addrType = indirectGotos.front().getAddr().getType();
92 Block *indirectGotoBlock = rewriter.createBlock(
93 &func.getBody(), func.getBody().end(), {addrType}, {loc});
94
97 for (StringRef name : addrTakenLabels) {
98 Block *dest = labels[name];
99 assert(dest && "address-taken label has no cir.label in this function");
100 successors.push_back(dest);
101 succOperands.push_back(dest->getArguments());
102 }
103 cir::IndirectBrOp::create(rewriter, loc, indirectGotoBlock->getArgument(0),
104 /*poison=*/false, succOperands, successors);
105
106 for (auto indirect : indirectGotos) {
107 mlir::OpBuilder::InsertionGuard guard(rewriter);
108 rewriter.setInsertionPoint(indirect);
109 cir::BrOp::create(rewriter, indirect.getLoc(), indirectGotoBlock,
110 indirect.getAddr());
111 indirect.erase();
112 }
113}
114
115void GotoSolverPass::runOnOperation() {
116 llvm::TimeTraceScope scope("Goto Solver");
117
118 // Block addresses also appear in attributes: global initializers and
119 // cir.const values. Collect, per target function and in walk order, the
120 // labels referenced this way so their LabelOps survive and join the
121 // indirect branch's successors. A SetVector keeps the first occurrence
122 // in order: a label named more than once is a successor only once.
123 //
124 // process() collects cir.block_address ops in IR order; this scan covers
125 // only attribute-carried addresses (globals, constants) that no
126 // function-body walk sees. BlockAddressOp stays out so op labels keep
127 // their IR-order phase instead of sorting with the attributes.
128 llvm::StringMap<llvm::SmallSetVector<StringRef, 4>> globalBlockAddrLabels;
129 mlir::ModuleOp module = mlir::cast<mlir::ModuleOp>(getOperation());
130 module.walk([&](mlir::Operation *op) {
131 if (!mlir::isa<cir::GlobalOp, cir::ConstantOp>(op))
132 return;
133 auto collectBlockAddresses = [&](mlir::Attribute attr) {
134 attr.walk(
135 [&](cir::BlockAddrInfoAttr info) {
136 globalBlockAddrLabels[info.getFunc().getValue()].insert(
137 info.getLabel());
138 },
139 [&](cir::BlockAddrDiffAttr diff) {
140 // A block-address difference attribute references two labels in
141 // the same function; keep both alive.
142 llvm::SmallSetVector<StringRef, 4> &labels =
143 globalBlockAddrLabels[diff.getFunc().getValue()];
144 labels.insert(diff.getLhsLabel().getValue());
145 labels.insert(diff.getRhsLabel().getValue());
146 });
147 };
148 op->getName().walkInherentAttrs(
149 op, [&](llvm::StringRef, mlir::Attribute &attr) {
150 collectBlockAddresses(attr);
151 });
152 });
153
154 static const llvm::SmallVector<StringRef> empty;
155 for (cir::FuncOp func : module.getOps<cir::FuncOp>()) {
156 auto it = globalBlockAddrLabels.find(func.getSymName());
157 process(func, it == globalBlockAddrLabels.end()
158 ? llvm::ArrayRef<StringRef>(empty)
159 : it->second.getArrayRef());
160 }
161}
162
163} // namespace
164
165std::unique_ptr<Pass> mlir::createGotoSolverPass() {
166 return std::make_unique<GotoSolverPass>();
167}
const internal::VariadicAllOfMatcher< Attr > attr
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
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
std::unique_ptr< Pass > createGotoSolverPass()