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 can also appear in attributes outside of any function body,
119 // such as global variable initializers. Collect, per target function and in
120 // initializer order, the labels referenced this way so their LabelOps survive
121 // and join the indirect branch's successors. A SetVector keeps the first
122 // occurrence in order: a label named more than once across initializers needs
123 // to be a successor only once.
124 llvm::StringMap<llvm::SmallSetVector<StringRef, 4>> globalBlockAddrLabels;
125 getOperation()->walk([&](mlir::Operation *op) {
126 for (const mlir::NamedAttribute &namedAttr : op->getAttrs()) {
127 namedAttr.getValue().walk([&](cir::BlockAddrInfoAttr info) {
128 globalBlockAddrLabels[info.getFunc().getValue()].insert(
129 info.getLabel());
130 });
131 // A block-address difference attribute references two labels in the same
132 // function; keep both alive.
133 namedAttr.getValue().walk([&](cir::BlockAddrDiffAttr diff) {
134 llvm::SmallSetVector<StringRef, 4> &labels =
135 globalBlockAddrLabels[diff.getFunc().getValue()];
136 labels.insert(diff.getLhsLabel().getValue());
137 labels.insert(diff.getRhsLabel().getValue());
138 });
139 }
140 });
141
142 static const llvm::SmallVector<StringRef> empty;
143 getOperation()->walk([&](cir::FuncOp func) {
144 auto it = globalBlockAddrLabels.find(func.getSymName());
145 process(func, it == globalBlockAddrLabels.end()
146 ? llvm::ArrayRef<StringRef>(empty)
147 : it->second.getArrayRef());
148 });
149}
150
151} // namespace
152
153std::unique_ptr<Pass> mlir::createGotoSolverPass() {
154 return std::make_unique<GotoSolverPass>();
155}
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()