CIRCT 23.0.0git
Loading...
Searching...
No Matches
SplitFuncs.cpp
Go to the documentation of this file.
1//===- SplitFuncs.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
11#include "mlir/Analysis/Liveness.h"
12#include "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/IR/Builders.h"
14#include "mlir/IR/Value.h"
15#include "mlir/Pass/Pass.h"
16#include "mlir/Transforms/RegionUtils.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/Support/MathExtras.h"
19#include <string>
20
21#define DEBUG_TYPE "arc-split-funcs"
22
23namespace circt {
24namespace arc {
25#define GEN_PASS_DEF_SPLITFUNCS
26#include "circt/Dialect/Arc/ArcPasses.h.inc"
27} // namespace arc
28} // namespace circt
29
30using namespace mlir;
31using namespace circt;
32using namespace func;
33
34//===----------------------------------------------------------------------===//
35// Pass Implementation
36//===----------------------------------------------------------------------===//
37
38namespace {
39struct SplitFuncsPass : public arc::impl::SplitFuncsBase<SplitFuncsPass> {
40 using arc::impl::SplitFuncsBase<SplitFuncsPass>::SplitFuncsBase;
41 void runOnOperation() override;
42 LogicalResult lowerFunc(FuncOp funcOp);
43
44 SymbolTable *symbolTable;
45};
46} // namespace
47
48void SplitFuncsPass::runOnOperation() {
49 symbolTable = &getAnalysis<SymbolTable>();
50 for (auto op : llvm::make_early_inc_range(getOperation().getOps<FuncOp>())) {
51 // Ignore extern functions
52 if (op.isExternal())
53 continue;
54 if (failed(lowerFunc(op)))
55 return signalPassFailure();
56 }
57}
58
59LogicalResult SplitFuncsPass::lowerFunc(FuncOp funcOp) {
60 if (splitBound == 0)
61 return funcOp.emitError("Cannot split functions into functions of size 0.");
62 unsigned numOps = 0;
63 for (Block &block : funcOp.getBody())
64 numOps += block.getOperations().size();
65 if (numOps < splitBound)
66 return success();
67 // SplitFuncs can only split a single-block region. Leave functions with
68 // multiple blocks untouched rather than failing the pass; splitting is a
69 // performance optimization, so bailing out benignly is preferable to
70 // erroring.
71 if (funcOp.getBody().getBlocks().size() > 1)
72 return success();
73 assert(funcOp->getNumRegions() == 1);
74 int numBlocks = llvm::divideCeil(numOps, splitBound);
75 OpBuilder opBuilder(funcOp->getContext());
76 SmallVector<Block *> blocks;
77 Block *frontBlock = &(funcOp.getBody().front());
78 blocks.push_back(frontBlock);
79 for (int i = 0; i < numBlocks - 1; ++i) {
80 SmallVector<Location> locs(frontBlock->getNumArguments(), funcOp.getLoc());
81 auto *block = opBuilder.createBlock(&(funcOp.getBody()), {},
82 frontBlock->getArgumentTypes(), locs);
83 blocks.push_back(block);
84 }
85
86 unsigned numOpsInBlock = 0;
87 SmallVector<Block *>::iterator blockIter = blocks.begin();
88 for (auto &op : llvm::make_early_inc_range(*frontBlock)) {
89 if (numOpsInBlock >= splitBound) {
90 ++blockIter;
91 numOpsInBlock = 0;
92 opBuilder.setInsertionPointToEnd(*blockIter);
93 }
94 ++numOpsInBlock;
95 // Don't bother moving ops to the original block
96 if (*blockIter == (frontBlock))
97 continue;
98 // Remove op from original block and insert in new block
99 op.remove();
100 (*blockIter)->push_back(&op);
101 }
103 // Move function arguments to the block that will stay in the function
104 for (unsigned argIndex = 0; argIndex < frontBlock->getNumArguments();
105 ++argIndex) {
106 auto oldArg = frontBlock->getArgument(argIndex);
107 auto newArg = blocks.back()->getArgument(argIndex);
108 replaceAllUsesInRegionWith(oldArg, newArg, funcOp.getBody());
109 }
110 Liveness liveness(funcOp);
111 auto argTypes = blocks.back()->getArgumentTypes();
112 auto args = blocks.back()->getArguments();
113
114 // We need to sort our liveness sets to avoid nondeterminism
115 auto sortFunc = [](Value a, Value b) {
116 auto *opA = a.getDefiningOp();
117 auto *opB = b.getDefiningOp();
118 if (opA == opB)
119 return cast<OpResult>(a).getResultNumber() <
120 cast<OpResult>(b).getResultNumber();
121 if (opA->getBlock() == opB->getBlock())
122 return opA->isBeforeInBlock(opB);
123 return false;
124 };
125
126 // Create return ops
127 for (int i = blocks.size() - 2; i >= 0; --i) {
128 liveness = Liveness(funcOp);
129 Block *currentBlock = blocks[i];
130 Liveness::ValueSetT liveOut = liveness.getLiveIn(blocks[i + 1]);
131 SmallVector<Value> outValues;
132 llvm::for_each(liveOut, [&outValues](auto el) {
133 if (!isa<BlockArgument>(el))
134 outValues.push_back(el);
135 });
136 llvm::stable_sort(outValues, sortFunc);
137 opBuilder.setInsertionPointToEnd(currentBlock);
138 ReturnOp::create(opBuilder, funcOp->getLoc(), outValues);
139 }
140 // Create and populate new FuncOps
141 for (long unsigned i = 0; i < blocks.size() - 1; ++i) {
142 Block *currentBlock = blocks[i];
143 Liveness::ValueSetT liveOut = liveness.getLiveIn(blocks[i + 1]);
144 SmallVector<Value> outValues;
145 llvm::for_each(liveOut, [&outValues](auto el) {
146 if (!isa<BlockArgument>(el))
147 outValues.push_back(el);
148 });
149 llvm::stable_sort(outValues, sortFunc);
150 auto outTypes = llvm::to_vector(
151 llvm::map_range(outValues, [](Value v) { return v.getType(); }));
152 opBuilder.setInsertionPoint(funcOp);
153 SmallString<64> funcName;
154 funcName.append(funcOp.getName());
155 funcName.append("_split_func");
156 funcName.append(std::to_string(i));
157 auto newFunc =
158 FuncOp::create(opBuilder, funcOp->getLoc(), funcName,
159 opBuilder.getFunctionType(argTypes, outTypes));
160 ++numFuncsCreated;
161 symbolTable->insert(newFunc);
162 auto *funcBlock = newFunc.addEntryBlock();
163 for (auto &op : make_early_inc_range(currentBlock->getOperations())) {
164 op.remove();
165 funcBlock->push_back(&op);
166 }
167 currentBlock->erase();
168
169 opBuilder.setInsertionPointToEnd(funcBlock);
170 for (auto [j, el] : llvm::enumerate(args))
171 replaceAllUsesInRegionWith(el, newFunc.getArgument(j),
172 newFunc.getRegion());
173 for (auto pair : argMap)
174 replaceAllUsesInRegionWith(pair.first, pair.second, newFunc.getRegion());
175 opBuilder.setInsertionPointToStart(blocks[i + 1]);
176 Operation *callOp = func::CallOp::create(opBuilder, funcOp->getLoc(),
177 outTypes, funcName, args);
178 auto callResults = callOp->getResults();
179 argMap.clear();
180 for (unsigned long k = 0; k < outValues.size(); ++k)
181 argMap.insert(std::pair(outValues[k], callResults[k]));
182 }
183 for (auto pair : argMap)
184 replaceAllUsesInRegionWith(pair.first, pair.second, funcOp.getRegion());
185 return success();
186}
assert(baseType &&"element must be base type")
Definition arc.py:1
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.